State 끌어올리기

1abme·2023년 5월 1일
0

역방향 데이터 흐름 추가


단방향 데이터 흐름이라는 원칙에 따라, 하위 컴포넌트는 상위 컴포넌트로부터 전달받은 데이터의 형태 혹은 타입이 무엇인지말 알 수 있다. 데이터가 어떻게 입력되었는지는 알지 못한다. 그러므로 하위 컴포넌트에서의 어떤 이벤트로 인한 상위 컴포넌트의 상태 변경은 마치 "역방향 데이터 흐름과" 같이 들릴 수 있다.

상태 끌어올리기 (state-lifting up)


React는 기본적으로 단방향 데이터 흐름이라는 원칙을 가진다. 상위 컴포넌트에서 하위로는 이동할 수 있지만 반대로는 불가하다. 그러나 종종 동일한 데이터에 대한 변경사항을 여러 컴포넌트에 반영할 필요가 생긴다.

이 때, state 끌어올리기 개념으로 데이터 변경사항을 상위 컴포넌트로 전달할 수 있다. 데이터를 직접 상위로 전달하는 것과는 다르게, state를 직접 전달하는 것이 아닌 state 갱신 함수를 전달 받아 해당 함수를 실행시키는 원리이다. 이는 React가 제시하는 "역방향 데이터 흐름"과 같은 흐름을 보이면서도 여전히 "단방향 데이터 흐름"의 원칙에 부합하는 해결 방법이다.

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

만약 동일한 데이터에 대한 변경사항을 여러 컴포넌트에 반영해야할 필요가 있을땐 가장 가까운 공통 조상(컴포넌트)에서 상태 끌어올리기를 하는 것을 권장한다.

// state-lifting up의 예시

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: "좋아 코드스테이츠!"
    }
  ]);

  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;

위의 예시 코드는
  1. NewTweetForm 에서 Props를 { onButtonClick } 으로 받아온다.

  2. onClick 이벤트가 발생시 onClickSubmit 함수가 실행된다.

  3. newTweettweets 형식에 맞는 객체로 세팅한다.

  4. NewTweetForm 에서 props로 받은 onButtonClick 의 전달인자로 newTweet을 넘겨 실행해준다.

  5. TwittleronButtonClick = {addNewTweet}addNewTweet 함수가 실행된다.

  6. addNewTweet에서 onButtonClick 의 전달인자 newTweet을 사용해 setTweets 함수를 실행한다.

  7. tweet[...tweets, newTweet]으로 갱신되어 SingleTweet 에 props가 전달된다.

  8. SingleTweet<li>가 값이 채워진채 리턴된다.

이렇게 진행되는 코드이며 몇 부분을 생략하고 아주 간단하고 비슷한 느낌만 나게끔 익숙한 javascript문법을 이용해 내 식대로 써보자면

function onButtonClick (newTweet) { // 4. `onButtonClick` 내부 함수의 전달인자로 넘기고 넘겨서 
  addNewTweet (newTweet) {
	setTweets(newTweet) {
      return tweets = [...tweets, newTweet] // 5. `tweets` 를 `newTweet` 을 포함한 배열로 리턴
    }
  }
}

btn.eventListener(onclick, function () { // 1. `onClick`	이벤트 발생시, 함수(== onClickSubmit) 실행
    let newTweet = { // 2. `newTweet` 을 생성한뒤
      ...생략
    };
    onButtonClick(newTweet) // 3. `onButtonClick` 함수에 전달인자로 `newTweet` 을 넘기고 실행
  };
)

이렇게 된다.

profile
제가 이해하고 있는게 맞나요...?

0개의 댓글

관련 채용 정보