✍️ wetagram을 만들면서 이전에 바닐라js로 기능 구현한 것들을 react로 적용해보자!
let [idValue, setIdValue] = useState('');
let [pwValue, setPwValue] = useState('');
const handleIdInput = e => {
setIdValue(e.target.value);
};
const handlePwInput = e => {
setPwValue(e.target.value);
};
<form className="loginForm" action="./main.html">
<input
type="text"
className="loginInputId"
placeholder="전화번호, 사용자 이름 또는 이메일"
onChange={handleIdInput}
/>
<input
type="password"
className="loginInputPw"
placeholder="비밀번호"
onChange={handlePwInput}
/>
<button
className="loginBtn"
onClick={goToMain}
disabled={
idValue.includes('@') && pwValue.length >= 5 ? false : true
}
>
로그인
</button>
</form>
input
과 pw input
에서 onChange
event가 발생한다.handleIdInput
함수와 handlePwInput
함수가 실행되면서 이벤트를 인자로 받는다.✍️ 구조분해 할당을 통해 아래처럼도 작성할 수도 있겠다. 😀
const handleIdInput = e => {
setIdValue(e.target.value);
};
const handlePwInput = e => {
setPwValue(e.target.value);
};
// ▼ 구조분해할당
const handleIdInput = ({ target: { value } }) => {
setIdValue(value);
};
const handlePwInput = ({ target: { value } }) => {
setPwValue(value);
};
<button
className="loginBtn"
onClick={goToMain}
disabled={
idValue.includes('@') && pwValue.length >= 5 ? false : true
}
@
가 포함되어있고 pw값의 길이가 5이상이면 false를 줌으로써 버튼을 활성화시킬 수 있다. 😀✍️ 시도 1)
const MainSb = () => {
const [comment, setComment] = useState('기본');
const [commnetArr, setCommentArr] = useState([
{ id: '1', userName: 'seul', comment: comment },
]);
const handleChange = e => { // 1-1)
e.preventDefault();
const commentValue = e.target.comment.value; // 3-1)
setComment(commentValue);
const copyArr = [...commnetArr];
if (commentValue !== '')
copyArr.push({ userName: 'seul', comment: comment });
setCommentArr(copyArr);
e.target.comment.value = '';
};
...
<form // 1)
className="commentForm"
onSubmit={handleSubmit}
onKeyUp={handleComment}
>
<img alt="smile icon" src="/images/seulbiKim/smile.png" />
<input
type="textarea"
name="comment" // 3)
placeholder="댓글달기..."
className="commentInput"
/>
<button className="commentBtn">게시</button>
</form>
...
// comment UI
<ul> // 2)
{commnetArr.map(commentData => (
<li key={commentData.id}>
<div className="commentsCommented">
<p>
<span>{commentData.userName}</span>{' '}
{commentData.comment}
</p>
</div>
<AiOutlineHeart className="articleDataIconsHeart" />
<AiFillHeart className="articleDataIconsHeart fill" />
<CgRemove className="articleDataIconsRemove" />
</li>
))}
</ul>
✍️ 결과적으로는 동기적으로 댓글이 달리지 않았다. value값을 따로 가져와 보자.
✍️ 시도 2)
📌 value값을 따로 받아오기
📌 고유한 Key 값 설정하기 & useRef() 사용하기
const [comment, setComment] = useState('');
const [commentArr, setCommentArr] = useState([]);
const nextId = useRef(1);
// 댓글 배열 저장
const handleSubmit = e => {
e.preventDefault();
const copyArr = [...commentArr];
if (e.target.comment.value !== '')
copyArr.push({ id: nextId.current, name: 'seul', comment: comment });
setCommentArr(copyArr);
e.target.comment.value = '';
nextId.current += 1;
};
// 코멘트 저장
const handleComment = e => {
e.preventDefault();
setComment(e.target.value);
};
// form
<form
className="commentForm"
onSubmit={handleSubmit}
onKeyUp={handleComment}
>
<img alt="smile icon" src="/images/seulbiKim/smile.png" />
<input
type="textarea"
name="comment"
placeholder="댓글달기..."
className="commentInput"
/>
<button className="commentBtn">게시</button>
</form>
<ul>
{commentArr.map(commentData => (
<li key={commentData.id}>
<div className="commentsCommented">
<p>
<span>{commentData.name}</span>{' '}
{commentData.comment}
</p>
</div>
<AiOutlineHeart className="articleDataIconsHeart" />
<AiFillHeart className="articleDataIconsHeart fill" />
<CgRemove className="articleDataIconsRemove" />
</li>
))}
</ul>
Array.map()
메소드를 적용해서 필요한 댓글 컴포넌트를 생성한다. 이 때 key값을 지정해 준다.useRef
훅을 이용해서 key값에 고유한 id를 지정해 주었다.❓ 왜 이렇게 따로 key를 설정해 주어야 할까? 🤔
▶ key props를 지정해주지 않으면 불필요한 렌더링이 발생될 수 있다.
📌 활용예시
// index.js
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<KeyTest />
</React.StrictMode>
);
// KeyTest.js
import React from 'react';
//NameList 컴포넌트(하위)
const NameList = ({ names }) => {
return <li>{`${names.name} hello !`}</li>;
};
// KeyTest 컴포넌트(상위)
const KeyTest = () => {
const names = [
{ id: 1, name: 'seul' },
{ id: 2, name: 'kim' },
{ id: 3, name: 'lee' },
];
const list = names.map((names) => <NameList names={names} key={names.id} />);
return <ul>{list}</ul>;
};
export default KeyTest;
📌 Check!
처음코드(수정전)
// Comment.js
const Comment = ({ commentData, commentRemove }) => {
const [commentLiked, setCommentLiked] = useState(false);
const clickedLike = () => {
setCommentLiked(isLiked => !isLiked);
};
return (
<li>
<div className="commentsCommented">
<p>
<span>{commentData.name}</span>
{commentData.comment}
</p>
</div>
{commentLiked === false ? (
<AiOutlineHeart
className="articleDataIconsHeart"
onClick={clickedLike}
/>
) : (
<AiFillHeart
className="articleDataIconsHeart fill"
onClick={clickedLike}
/>
)}
<CgRemove
className="articleDataIconsRemove"
id={commentData.id}
onClick={commentRemove}
/>
</li>
);
};
// FeedArticle.js
const commentRemove = e => {
const removeId = parseInt(e.target.id);
const filtered = commentList.filter(comment => comment.id !== removeId);
setCommentList(filtered);
};
✍️ 발생된 문제점
❓ 이유는 svg를 클릭할 때와 svg내 path를 클릭 했을 때가 달랐기 때문이다.
svg를 클릭하면 e.target.id
가 맞게 출력되지만, path를 클릭하면 e.target.id
가 NaN
이 발생된다.
❗️이때 피해야할 점은 컴포넌트에 id를 주는 것은 좋지 않다. 컴포넌트 재사용성을 떨어트리기 때문이다.
1차 수정
// FeedArticle.js
const commentRemove = e => {
console.log('target', e.target);
console.log('current', e.currentTarget);
const removeId = parseInt(e.currentTarget.id);
const filtered = commentList.filter(comment => comment.id !== removeId);
setCommentList(filtered);
console.log(removeId);
console.log(commentList);
};
currentTarget
은 내가 그 버튼을 눌렀을 때, svg나 path 따라서 target이 설정되는 것이 아니고, 그 evnet가 달린 컴포넌트가 선택되기 때문에 e.target.id
가 아니고 e.currentTarget.id
으로 설정하면 된다.2차 수정
// FeedArticle.js
const commentRemove = id => {
const removeId = parseInt(id);
const filtered = commentList.filter(comment => comment.id !== removeId);
setCommentList(filtered);
};
// Comment.js
<CgRemove
className="articleDataIconsRemove"
// id={commentData.id}
onClick={deleteComment}
/>
id={commentData.id}
를 지정해서 사용하지 않고 받아온 commentData
의 id
를 바로 넘겨주면서 deleteComment 함수를 호출한다.onClick={() => commentRemove(commentData.id)}