상태 끌어올리기
- 단방향 데이터 흐름이라는 원칙에 따라, 하위 컴포넌트는 상위 컴포넌트로부터 전달받은 데이터의 형태 혹은 타입이 무엇인지만 알 수 있음. 데이터가 어디서왔는지 등은 모름!
- 그런데 하위 컴포넌트에서의 어떤 이벤트로 인해 상위 컴포넌트의 상태가 바뀌는 것은 "역방향"이 아닌가 의문임.
여기서 발상의 전환!
상위 컴포넌트의 "상태를 변경하는 함수" 그 자체를 하위 컴포넌트로 전달하고, 이 함수를 하위 컴포넌트가 실행한다
이것이 상태 끌어 올리기임.
예제
export default function ParentComponent() {
const [value, setValue] = useState("날 바꿔줘!");
const handleChangeValue = () => {
setValue("보여줄게 완전히 달라진 값");
};
return (
<div>
<div>값은 {value} 입니다</div>
<ChildComponent />
</div>
);
}
function ChildComponent() {
const handleClick = () => {
// 이 버튼을 눌러서 부모의 상태를 바꿀 순 없을까?
// 인자로 받은 상태 변경 함수를 실행시키자!
};
return <button onClick={handleClick}>값 변경</button>;
}
- 상태를 변경하는 함수는 handleChangeValue이다.
전달은 props를 이용해 전달하고 하위 컴포넌트가 버튼 클릭 이벤트에 따라 상태를 변경하려고 하므로 이름은 handleButtonClick이라 부르자
function ParentComponent() {
const [value, setValue] = useState("날 바꿔줘!");
const handleChangeValue = () => {
setValue("보여줄게 완전히 달라진 값");
};
return (
<div>
<div>값은 {value} 입니다</div>
<ChildComponent handleButtonClick={handleChangeValue} /> // 변경!
</div>
);
}
//... 생략
"상태 변경 함수"는 버튼이 클릭할때 실행되기를 원하므로, 해당 부분에 콜백 함수를 실행시키자.
function ChildComponent({ handleButtonClick }) { // 이거 {} 없으면 실행이 안됨
// {}없으면 콘솔창에 {yes: ƒ handleChangeValue()} {} 있으면 ƒ handleChangeValue()
const handleClick = () => {
// Q. 이 버튼을 눌러서 부모의 상태를 바꿀 순 없을까?
// A. 인자로 받은 상태 변경 함수를 실행하자!
handleButtonClick()
}
return (
<button onClick={handleClick}>값 변경</button>
)
}
필요에 따라 설정할 값을 콜백 함수의 인자로 넘길 수 있음
function ParentComponent() {
const [value, setValue] = useState("날 바꿔줘!");
const handleChangeValue = (newValue) => {
setValue(newValue);
};
// ...생략...
}
function ChildComponent({ handleButtonClick }) {
const handleClick = () => {
handleButtonClick('넘겨줄게 자식이 원하는 값')
}
return (
<button onClick={handleClick}>값 변경</button>
)
}
예제2
어떻게 <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} /> // addNewTweet 이란 함수를
// onButtonClick이라는 이름으로 NewTweetForm에 전송
<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
};
onButtonClick(newTweet) // 받아서 사용!
// TDOO: 여기서 newTweet이 addNewTweet에 전달되어야 합니다.
};
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;
'코딩공부 > T.I.L' 카테고리의 다른 글
2022-02-07 Effect Hook (1) - 기본 (0) | 2022.02.07 |
---|---|
2022-02-07 Side Effect (0) | 2022.02.07 |
2022-02-07 [React] 데이터 흐름의 이해와 비동기 요청 처리 성취 목표 (0) | 2022.02.07 |
2022-01-28 Message States Server REST API (0) | 2022.01.28 |
2022-01-28 Postman으로 날씨 받아오기 (0) | 2022.01.28 |