(React) 상태 끌어올리기 (Lifting State Up)

Yunu·2022년 8월 9일
post-thumbnail

배경지식

리액트는 컴포넌트 단위로 제작됩니다.
리액트는 단방향 데이터 흐름을 갖고 있습니다.
자식 컴포넌트는 부모 컴포넌트에서 props를 이용해 데이터를 마치 전달인자(arguments) 혹은 속성(attributes)처럼 전달받을 수 있습니다.
데이터 흐름이 하향식(top-down)으로 진행됨

용도

하위 컴포넌트에서의 어떤 이벤트로 인해 상위 컴포넌트의 상태가 바뀌는 경우 단방향 데이터 흐름을 유지하기 위해 사용함
EX) 새로운 트윗 추가

사용 방법

상위 컴포넌트의 "상태를 변경하는 함수" 그 자체를 하위 컴포넌트로 전달하고, 이 함수를 하위 컴포넌트가 실행한다

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>
  );
}
// 상태를 변경하는 함수는 handleChangeValue 입니다. 
// 전달은 props를 이용합시다. props 이름은 적절하게 지어줍시다. 
// 하위 컴포넌트가 버튼 클릭 이벤트에 따라 상태를 변경하려고 하므로 이름은 handleButtonClick이라고 지어줍시다.

function ChildComponent({handleButtonClick}) {
  const handleClick = () => {
    // 이 버튼을 눌러서 부모의 상태를 바꿀 순 없을까?
    handleButtonClick();
  };
// <ChildComponent>는 마치 고차 함수가 인자로 받은 함수를 실행하듯, 
// props로 전달받은 함수를 컴포넌트 내에서 실행할 수 있게 됩니다. 
// "상태 변경 함수"는 버튼이 클릭할 때 실행되기를 원하므로, 해당 부분에 콜백 함수를 실행합시다.

  return <button onClick={handleClick}>값 변경</button>;
}

다른 예제

트위터에 새로운 트윗을 작성하는 경우

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)
  };

  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;

0개의 댓글