State 끌어올리기 - 주요 개념

hongregii·2023년 3월 1일
0

이 문서가 바로 블로그 개설 계기.

리액트를 공부한다면 반드시 알아야 할 개념이고, 말로 설명을 들으면 "음~ 그렇지" 라고 하지만, 정작 "상태 끌어올리기를 설명해주세요" 라고 하면 말문이 막힌다.

꺼진 공식문서도 다시 보고, 기본 개념을 더 정확히, 자세히 알아가자.

기존 공식문서는 설명도 복잡하고 클래스 컴포넌트 기반이라, Beta 공식문서 기준으로 정리하겠음.

Single Source of Truth

진리의 원천은 하나로

하나의 state를 여러 component에서 보여주고, 그 중 하나에서 state가 바뀌면 다른 component에 뿌려준 state가 동시에 바뀌어야 하는 경우가 많다.

여러 component에 뿌려져 있더라도, 그 state가 선언된 곳은 하나여야 한다. 그 state를 소유하는 component를 정해야 하고, 그건 가장 가까운 공통 부모 component 인 것이 좋다.

순서

  1. 가장 가까운 공통 부모에 state 선언
    = 아래에 선언하지 말고 lift it up, 끌어올려라

  2. 필요한 각 component에 state를 props 형태로 내려줌

  3. event handler 함수를 같이 내려준다
    event handler 함수는 state와 같은 컴포넌트에 선언돼야 함.
    = 자식 component가 부모의 state를 바꿀 수 있게끔

    Accordion 예시

    공식문서에서 예시를 잘 설명해둬서, 그대로 쓰겠음

    끌어올리기 안한 경우

    각 state가 따로 선언된 셈이다.

    
    import { useState } from 'react';
    // Panel (자식 컴포넌트) 선언
    const Panel = ({ title, children }) => {
     // state를 자식에 선언
     const [isActive, setIsActive] = useState(false);
    
      return (
       <section className="panel">
         <h3>{title}</h3>
    // state가 true면 그려줘
         {isActive ? (
           <p>{children}</p>
         ) : (
           <button onClick={() => setIsActive(true)}> // 버튼 누르면 state true로 바꿈
             Show
           </button>
         )}
       </section>
     );
    }
    
    const Accordion = () => {
     return (
       <>
         <h2>Almaty, Kazakhstan</h2>
      
       // 위에서 선언한 Panel 컴포넌트 자식으로 두개
         <Panel title="About">
           With a population of about 2 million, Almaty is Kazakhstan's largest city. From 1929 to 1997, it was its capital city.
         </Panel>
       
         <Panel title="Etymology">
           The name comes from <span lang="kk-KZ">алма</span>, the Kazakh word for "apple" and is often translated as "full of apples". In fact, the region surrounding Almaty is thought to be the ancestral home of the apple, and the wild <i lang="la">Malus sieversii</i> is considered a likely candidate for the ancestor of the modern domestic apple.
         </Panel>
       </>
     );
    }

마크다운에 react 어떻게 임포트해올지를 모르겠다... 일단 설명으로 대체

위 코드를 그림으로 표현하면 이렇다.

  • Accordion은 부모, Panel은 자식.
  • 자식에 isActive 라는 state 각각 선언.
  • Panel 안에 있는 버튼 누르면 Panel 안에 있는 state가 true 로 바뀜.
  • 두 state는 이름은 같지만 서로 상관이 없음. 따로 움직임

구현하고 싶은 것 : 한 Panel이 펴지면 다른 Panel이 닫히게

  • isActive라는 state는 자식에서 지워야 하고, 부모에 새로운 state가 선언돼야 함.
  • 어떤 Panel이 열려있는지 알기 위해, state를 bool 말고 index값 (숫자)로 선언하면 좋을 것임.
  • 버튼은 자식에 있기 때문에, state값을 바꾸는 event handler를 부모에 선언한 뒤, 각 Panel에 내려줘야 함.
// 부모 (Accordion) 컴포넌트

// 초기값을 index값으로 선언 (0).
// 0이면 첫번째 Panel open 시킬것임.
const [activeIndex, setActiveIndex] = useState(0);
// 자식 (Panel) 컴포넌트

// 받은 activeIndex state가 0이면 첫번째 isActive는 true, 두번째는 false.
// 그렇다면 첫번째 Panel이 펴지고 두번째는 닫혀있을것.
// onShow는 event handler 함수이다.
<>
  <Panel
    isActive={activeIndex === 0}
    onShow={() => setActiveIndex(0)}
  >
    ...
  </Panel>
  <Panel
    isActive={activeIndex === 1}
    onShow={() => setActiveIndex(1)}
  >
    ...
  </Panel>
</>
  • isActive는 내려준 state값이 무엇인지에 따라 true 또는 false가 됨.
  • onShow 는 event handler 함수다. Panel은 이 함수를 prop으로 받았다.
  • Panel에서 받은 onShow 함수를 버튼에 넣어준다.
const Panel = ({
  title,
  children,
  isActive,
  onShow
}) => {
  return (
    <section className="panel">
      <h3>{title}</h3>
      {isActive ? (
        <p>{children}</p>
      ) : (
        <button onClick={onShow}> // 여기서 !
          Show
        </button>
      )}
    </section>
  );
}

이제 형제 component에서 버튼을 눌러도, state가 부모에서 선언되어 각각 내려줬기 때문에 다른 형제에서도 동시에 바뀐다!

profile
잡식성 누렁이 개발자

0개의 댓글