React는 기본적으로 단방향 데이터 흐름(하향식)을 따라 상위 컴포넌트에서 하위로는 전달할 수 있지만, 상향식(아래 → 위)으로는 전달할 수 없다.
하위 컴포넌트는 상위 컴포넌트로부터 전달받은 데이터의 형태 or 타입이 무엇인지만 알 수 있다.
(데이터가 state로부터 왔는지, 하드코딩으로 입력한 내용인지 알지 못함)
종종 동일한 데이터에 대한 변경사항을 여러 컴포넌트에 반영할 필요가 생긴다. 이 때, 하위 컴포넌트에서 데이터를 직접 상위로 전달하는 것은 단방향 데이터 흐름의 원칙에 부합하지 않는다.
단방향 데이터 흐름의 원칙에 부합하면서 하위 컴포넌트를 통해(클릭 이벤트 등) 상위 컴포넌트의 상태(State)를 변경하기 위해서는 다음과 같은 방법을 이용할 수 있다.
아래의 예제와 같은 방법을 상태 끌어올리기 라고 한다.
ChildComponent 컴포넌트에서 ParentComponent의 value 상태를 변경하기
import React, { useState } from "react";
export default function ParentComponent() {
const [value, setValue] = useState("날 바꿔줘!");
const handleChangeValue = () => {
setValue("보여줄게 완전히 달라진 값");
};
return (
<div>
<div>값은 {value} 입니다</div>
{/* 하위 컴포넌트에 상태 변경 함수 자체를 props로 전달 */}
<ChildComponent buttonClick={handleChangeValue}/>
</div>
);
}
function ChildComponent({buttonClick}) { // 구조분해할당으로 상위 컴포넌트의 props 전달받기
const handleClick = () => {
// 전달받은 상태 변경 함수를 실행
buttonClick();
};
return <button onClick={handleClick}>값 변경</button>;
}
NewTweetForm 컴포넌트에서 Twittler의 tweets 상태를 변경하기
import React, { useState } from "react";
const currentUser = "김코딩";
function Twittler() {
const [tweets, setTweets] = useState([
{
uuid: 1,
writer: "김코딩",
date: "2020-10-10",
content: "안녕 리액트"
},
{
uuid: 2,
writer: "박해커",
date: "2020-10-12",
content: "좋아 코드스테이츠!"
}
]);
// tweets 상태 변경 함수
const addNewTweet = (newTweet) => {
setTweets([...tweets, newTweet]);
}; // 이 상태 변경 함수가 NewTweetForm에 의해 실행되어야 한다
return (
<div>
<div>작성자: {currentUser}</div>
{/* 하위 컴포넌트에 상태 변경 함수 자체를 props로 전달 */}
<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 }) { // 상위 컴포넌트로부터 props 전달받기
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
};
// 여기서 newTweet이 addNewTweet에 전달되어야 한다.
// 전달받은 상태 변경 함수를 실행
onButtonClick(newTweet);
};
return (
<div id="writing-area">
<textarea id="new-tweet-content" onChange={onTextChange}></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;