Twittler
에서 컴포넌트 디자인을 해보자.props
를 이용해 데이터를 마치 전달인자(arguments) 혹은 속성(attributes)처럼 전달받을 수 있다.props
를 통해 전달받은 데이터가 어디서 왔는지 전혀 알지 못한다.state
)로, 변하지 않는 값은 props
로 관리할 수 있다.
- 부모로부터
props
를 통해 전달되는가? ➡ 그러면 확실히state
가 아니다.- 시간이 지나도 변하지 않는가? ➡ 그러면 확실히
state
가 아니다.- 컴포넌트 안의 다른
state
나props
를 가지고 계산 가능한가? ➡ 그렇다면state
가 아니다.
state
와 props
를 관리하여 React 애플리케이션을 구성할 수 있다.state
) 위치 정하기Tweets
: 개별 트윗을 출력하기 위해 필요NewTweetForm
: 새 글을 추가하는 이벤트가 발생할 경우 전체 트윗 목록에 새로운 트윗 객체를 추가하기 위해 필요Twittler
에 "전체 트윗 목록" 상태를 위치시킨다.NewTweetForm
은 버튼이 눌린 후 완성된 하나의 트윗 객체를 전체 트윗 목록에 전달하기만 하면 되기 때문에 다른 컴포넌트와 공유할 필요가 없다.NewTweetForm
에 두는 것으로 충분하다.props
로 전달하는 것으로, 마치 콜백 함수를 사용하는 방법과 비슷하다.NewTweetForm
)에서의 클릭 이벤트가 부모의 상태를 바꾸어야 하는 상황에서 이를 해결하기 위해 State 끌어올리기를 사용할 수 있다.state
)를 변경해야 하는 경우, 상위 컴포넌트의 상태를 변경하는 함수(handler
)를 하위 컴포넌트로 전달하고,예제 앱은 부모와 자식 컴포넌트가 하나씩 존재하는 트리 구조이다.
그리고, 아래와 같은 상태를 변경시킬 수 있는 메서드가 존재한다.
import React, { useState } from "react";
export default function ParentComponent() {
const [value, setValue] = useState("날 바꿔줘!");
const handleChangeValue = () => {
setValue("보여줄게 완전히 달라진 값");
};
return (
<div>
<div>값은 {value} 입니다</div>
<ChildComponent handleButtonClick={handleChangeValue} />
</div>
);
}
function ChildComponent({ handleButtonClick }) {
const handleClick = () => {
// 이 버튼을 눌러서 부모의 상태를 바꿀 순 없을까?
handleButtonClick();
};
return <button onClick={handleClick}>값 변경</button>;
}
ParentComponent
는 부모 컴포넌트 역할을 하며 상태 값(value
)과 상태를 변경하는 함수(handleChangeValue
)를 가지고 있다.
ChildComponent
는 ParentComponent
의 자식 컴포넌트 역할을 하며, ParentComponent
로부터 전달받은 상태를 변경하는 함수(handleButtonClick
)를 버튼 클릭 이벤트에 따라 실행시킨다.
상태를 변경하는 함수(handleChangeValue
)는 부모 컴포넌트인 ParentComponent
에서 정의하고, 자식 컴포넌트인 ChildComponent
에서 실행된다.
이렇게 상위 컴포넌트에서 하위 컴포넌트로 함수를 전달하여 하위 컴포넌트에서 실행하는 것을 "상태 끌어올리기" 패턴이라고 한다.
결과적으로, ChildComponent
에서 버튼을 클릭하면, handleButtonClick
함수가 실행되고, 이는 ParentComponent
에서 정의한 handleChangeValue
함수를 실행하게 된다.
이를 통해 상태가 변경되고, ParentComponent
가 다시 렌더링되어 변경된 상태를 화면에 보여줄 수 있게 된다.
다음 예제 코드는 작성자와 글 내용을 입력받아 새로운 트윗을 추가하는 기능을 가진 코드이다.
어떻게 <NewTweetForm>
컴포넌트에서 tweets
상태를 변화시킬 수 있을까?
(어떻게 State 끌어올리기를 적용할 수 있을까?)
import React, { useState } from "react";
import "./styles.css";
const currentUser = "김코딩";
function Twittler() {
const [tweets, setTweets] = useState([
{
uuid: 1,
writer: "김코딩",
date: "2020-10-10",
content: "안녕 리액트"
},
{
uuid: 2,
writer: "박해커",
date: "2020-10-12",
content: "좋아 코드스테이츠!"
}
]);
const addNewTweet = (newTweet) => {
setTweets([...tweets, newTweet]);
}; // 이 상태 변경 함수가 NewTweetForm에 의해 실행되어야 한다.
return (
<div>
<div>작성자: {currentUser}</div>
<NewTweetForm onButtonClick={addNewTweet} />
<ul id="tweets">
{tweets.map((t) => (
<SingleTweet key={t.uuid} writer={t.writer} date={t.date}>
{t.content}
</SingleTweet>
))}
</ul>
</div>
);
}
function NewTweetForm({ onButtonClick }) {
const [newTweetContent, setNewTweetContent] = useState("");
const onTextChange = (e) => {
setNewTweetContent(e.target.value);
};
const onClickSubmit = () => {
let newTweet = {
uuid: Math.floor(Math.random() * 10000),
writer: currentUser,
date: new Date().toISOString().substring(0, 10),
content: newTweetContent
};
// TDOO: 여기서 newTweet이 addNewTweet에 전달되어야 한다.
onButtonClick(newTweet);
setNewTweetContent("");
};
return (
<div id="writing-area">
<textarea
id="new-tweet-content"
onChange={onTextChange}
value={newTweetContent}
></textarea>
<button
id="submit-new-tweet"
onClick={onClickSubmit}>
새 글 쓰기
</button>
</div>
);
}
function SingleTweet({ writer, date, children }) {
return (
<li className="tweet">
<div className="writer">{writer}</div>
<div className="date">{date}</div>
<div>{children}</div>
</li>
);
}
export default Twittler;
NewTweetForm
컴포넌트에서 입력된 새로운 트윗을 Twittler
컴포넌트의 상태인 tweets
배열에 추가해야 한다.Twittler
컴포넌트에서 상태 변경 함수인 addNewTweet
을 정의하고, 이 함수를 NewTweetForm
컴포넌트의 props
로 전달한다.NewTweetForm
컴포넌트에서는 버튼 클릭 이벤트가 발생할 때, 입력된 내용과 현재 작성자 정보를 포함하는 새로운 트윗 객체를 생성하고, 이를 Twittler
컴포넌트에서 전달받은 addNewTweet
함수를 호출하여 상태를 변경한다.