동일한 데이터에 대한 변경사항을 여러 컴포넌트에 반영해야 할 필요가 있다.
이럴 때에는 가장 가까운 공통 조상으로 state를 끌어올리는게 좋다. -React 공식문서-
state끌어올리기는 그럼 왜 하는가? => 동기화!!
Props
vs State
props
: 고정값(기본값으로 사용), READ ONLY
state
: 가변값, 사용자가 이벤트 요청시 변경 (수정 가능)
props
는 스마트폰의 볼륨버튼이라면 사용자가 볼륨버튼을 누르면state
는 스마트폰안에서 스스로의 상태인 볼륨이 바뀌게 해놓은 모든 조치라고 할 수 있다. 상위 Component
는 하위 Component
에게 props
를 통해 값을 전달하여 내부의 state
를 바꾸기 때문에 컴포넌트 스스로 외부에서 전달되는 props를 변경하는 것은 금지되어 있다.const Feed = props => {
const { userName, userAvatar, feedImg, content, likeHit, comments } = props;
const [feedComments, setFeedComments] = useState(comments);
const commentLenght = feedComments.length;
const addNewComment = newComment => { // 자식에게 전달해줄 함수
setFeedComments([...feedComments, newComment]);
};
const deleteComment = comment => { // 자식에게 전달해줄 함수
const copyFeedComments = [...feedComments];
const comments = copyFeedComments.filter(
feedComment => feedComment.id !== comment.id
);
setFeedComments(comments);
};
return (
...
<div className="feed-wrapper">
<section className="feed-bottom">
<div className="feed-bottom-info">
<FeedDesc
userName={userName}
content={content}
comments={feedComments}
onDeleteButtonClick={deleteComment} // deleteComment 함수를 props로 전달
/>
</div>
</section>
<CommentInput
onButtonClick={addNewComment} // addNewComment 함수를 props로 전달
commentLenght={commentLenght}
/>
</div>
);
};
export default Feed;
import React, { useRef, useState } from 'react';
const CommentInput = props => {
const { onButtonClick, commentLenght } = props; // 부모에게 받아온 Click이벤트 변수에 저장
const [newCommentContent, setNewCommentContent] = useState('');
const onClickSubmit = () => {
const newComment = {
id: commentLenght + 1,
userName: 'hello._.',
content: newCommentContent,
};
onButtonClick(newComment); // submit이 될 때 부모에게서 받아온 onButtonClick에 새로운 코멘트를 넣어 전달해준다.
inputRef.current.value = '';
};
return (
<section className="comment-wrapper">
<button type="button">
<i className="far fa-smile" />
</button>
<input
name="comment"
type="text"
placeholder="댓글 달기..."
onChange={e => onTextChange(e)}
onKeyDown={e => {
if (e.code === 'Enter') onClickSubmit();
}}
ref={inputRef}
/>
<button
className="comment-submit-button button-primary"
disabled={buttonSwitch}
onClick={onClickSubmit}
>
게시
</button>
</section>
);
};
export default CommentInput;
const addNewComment = newComment => { // 자식에게 전달해줄 함수
setFeedComments([...feedComments, newComment]);
};
<CommentInput
onButtonClick={addNewComment} // addNewComment 함수를 props로 전달
commentLenght={commentLenght}
/>
const onClickSubmit = () => {
const newComment = {
id: commentLenght + 1,
userName: 'hello._.',
content: newCommentContent,
};
onButtonClick(newComment); // submit이 될 때 부모에게서 받아온 onButtonClick에 새로운 코멘트를 넣어 전달해준다.
inputRef.current.value = '';
};
onButtonClick(newComment)
가 실행되면서 부모에 있던 addNewComment
함수가 실행!setFeedComments([...feedComments], newComment)
를 통해 comment state 다시 셋팅 const deleteComment = comment => {
const copyFeedComments = [...feedComments];
const comments = copyFeedComments.filter(
feedComment => feedComment.id !== comment.id
);
setFeedComments(comments);
};
<Feed />
에서 <FeedDesc />
에게 onDeleteButtonClick = {deleteComment}
로 함수 전달<FeedDesc />
에서 <CommentList />
에게 또 함수 전달<ul className="feed-comment-list">
{comments.map(comment => (
<CommentList
key={comment.id}
comment={comment}
onDeleteButtonClick={onDeleteButtonClick}
/>
))}
</ul>
<CommentList />
에서 삭제할 comment onDeleteButtonClick
함수 에게 전달하며 실행<button
type="button"
onClick={() => {
// delete target comment
onDeleteButtonClick(comment);
}}
>
onDeleteButtonClick(comment)
가 실행되며 코멘트 삭제 const deleteComment = comment => {
const copyFeedComments = [...feedComments];
const comments = copyFeedComments.filter(
// 내가 선택한 id가 아닌 것들만 comments 에 다시 집어 넣어주기
feedComment => feedComment.id !== comment.id
);
setFeedComments(comments);
};