sohyeon kim

[React] 리액트 공식문서 정리 3(2) : State 관리하기 - 컴포넌트 간 state 공유, 상태 보존 및 초기화, key 활용 본문

React

[React] 리액트 공식문서 정리 3(2) : State 관리하기 - 컴포넌트 간 state 공유, 상태 보존 및 초기화, key 활용

aotoyae 2024. 12. 6. 14:17
728x90

 

 

💡 리액트 공식문서 정리 : State 관리하기 - 컴포넌트 간 State 공유하기, State 를 보존하고 초기화하기

 

3. 컴포넌트 간 State 공유하기 : state 를 부모 컴포넌트로 끌어올려 props 로 내려준다.

두 컴포넌트의 state 가 항상 함께 변경되기를 원할 때

➡️ 각 컴포넌트에서 state 를 제거하고 가장 가까운 공통 부모 컴포넌트로 옮겨 props 로 전달한다.

 

아래에서 각 패널은 독립적인 state 를 가지고 있어 show 버튼을 눌러도 다른 패널에 영향을 미치지 않는다.

function Panel({ title, children }) { // 비제어 컴포넌트. 부모로무터 제어되지 않는다.
  const [isActive, setIsActive] = useState(false);

 

이제 한 번에 하나의 패널만 열리도록 변경해 보자.

부모 컴포넌트로 state 를 옮기고 Panel 에는 props 를 내려준다.

export default function Accordion() {
  const [activeIndex, setActiveIndex] = useState(0);
  return (
    <>
      <h2>Almaty, Kazakhstan</h2>
      <Panel
        title="About"
        isActive={activeIndex === 0}
        onShow={() => setActiveIndex(0)}
      >
        With a population //..
      </Panel>
      <Panel
        title="Etymology"
        isActive={activeIndex === 1}
        onShow={() => setActiveIndex(1)}
      >
        The name comes from //..
      </Panel>
    </>
  );
}

function Panel({ title, children, isActive, onShow }){ // 제어 컴포넌트.
//..
}

컴포넌트 간의 공유된 state 도 중복되지 않게 그들의 공통 부모로 끌어올리고 필요한 자식에게 전달하자.

작업이 진행되면서 애플리케이션은 계속 변한다. 각 state가 어디에 '생존'해야 할지 고민하는 동안 state를 아래로 이동하거나 다시 올리는 것은 흔히 있는 일다. 이건 모두 과정의 일부이다!

 

4. State 를 보존하고 초기화하기 : React 는 언제, 어떻게 state 를 컨트롤하는가?

React 는 UI 트리에서의 위치를 통해 각 State 가 어떤 컴포넌트에 속하는지 추척한다.

 

State 는 렌더 트리의 위치에 연결된다.

  • React 는 UI 안에 있는 컴포넌트 구조로 렌더 트리를 만든다.
  • UI 트리에 있는 컴포넌트의 위치를 이용해 React 가 가지고 있는 각 State 를 알맞은 컴포넌트와 연결한다.
    • 컴포넌트에 state 를 줄 때 state 가 컴포넌트 안에 '살고' 있다고 생각할 수 있다.
      하지만 사실 state 는 React 안에 있다.

 

여기 동일한 JSX 태그가 다른 두 군데에서 렌더링되고 있다.

 

이 둘은 각각 트리에서 자기 고유의 위치에 렌더링되어 있으므로 분리되어있는 카운터다.

카운터를 클릭해도 다른 컴포넌트에 영향일 끼지지 않는다.

 

❗️ React 는 트리의 동일한 컴포넌트를 동일한 위치에 렌더링하는 동안 상태를 유지한다.

 

두 번째 카운터가 렌더링되지 않았다가 다시 나타나면 state 가 초기화된 것을 확인할 수 있다.

이는 React 가 컴포넌트를 제거할 때 그 state 도 같이 제거하기 때문이다.

 

❗️ 그러므로 같은 자리의 같은 컴포넌트는 state 를 보존한다.

 

여기 서로 다른 두 <Counter /> 태그를 보자.

체크 박스를 선택하거나 해제할 때 카운터 state 는 초기화되지 않는다.

isFancy 가 true 이든 false 이든 <Counter /> 는 같은 자리에 있기 때문. (root App 컴포넌트가 반환한 div의 첫 번째 자식으로)

첫 번째 자리의 같은 컴포넌트이기 때문에 React 의 관점에선 같은 카운터이다. (React 는 UI 트리에서의 위치에 관심이 있다.)

 

 

같은 위치의 다른 컴포넌트는 state 를 초기화한다.

 

아래처럼 다른 태그인 p 로 교체되거나 같은 카운터라도 감싸고 있는 태그가 달라지면

React 는 다른 컴포넌트로 인식해 컴포넌트와 state 가 제거된다.

🙋‍♂️ 리렌더링 시 state 를 유지하고 싶다면, 트리 구조가 '같아야'한다.

 

같은 위치에서 state 초기화하기

 

기본적으로 React 는 컴포넌트가 같은 위치를 유지하면 state 를 유지한다.

하지만 컴포넌트의 state 를 초기화하고 싶다면?

다음 플레이어로 넘어가도 스코어가 유지되고 있다.

 

1️⃣ 컴포넌트를 다른 위치에 렌더링하기

  return (
    <div>
      {isPlayerA &&
        <Counter person="Taylor" />
      }
      {!isPlayerA &&
        <Counter person="Sarah" />
      }

 

처음엔 isPlayerA 가 true 이므로, 첫 번째 자리에 Counter 가 있고 두 번째 자리는 비어있다.

'Next player' 를 클릭하면 첫 번째 자리가 비워지고 두 번째 자리에 Counter 가 온다.

(이 방법은 같은 자리에 적은 수의 독립된 컴포넌트만을 가지고 있을 때 편리하다. 이 예시에선 두 개뿐이기에 JSX 에서 각각 렌더링하기 번거롭지 않았다.)

2️⃣ key 를 이용해 state 초기화하기

배열을 렌더링할 때 key 를 사용했을 것이다. 하지만 key 는 배열을 위한 것만은 아니다!

React 가 컴포넌트를 구별할 수 있도록 key 를 사용할 수도 있다.

 

기본적으로 React 는 컴포넌트를 구별하기 위해 부모 안에서의 순서('첫 번째 카운터', '두 번째 카운터')를 이용한다.

하지만 key 를 명시하면 React 는 부모 내에서의 순서 대신 key 자체를 위치의 일부로 사용한다.

위치가 같아도 React 관점에선 다른 카운터인 것!

예를 들면 Taylor 의 카운터처럼. 이렇게 하면 트리 어디에서 나타나는 React 는 Taylor 의 카운터라는 것을 알 수 있다.

  return (
    <div>
      {isPlayerA ? (
        <Counter key="Taylor" person="Taylor" />
      ) : (
        <Counter key="Sarah" person="Sarah" />
      )}

 

key 가 다르기 때문에 두 <Counter /> 는 JSX 에서 같은 위치에 나타나지만, state 를 공유하지는 않는다.

❗️ key 는 전역적으로 유일하지 않다. key 는 오직 부모 안에서만 자리를 명시한다.

 

key 를 이용해 폼 초기화하기

key 로 state 를 초기화하는 것은 특히 폼을 다룰 때 유용하다.

다음 채팅 앱에서 <Chat> 컴포넌트는 문자열 입력 state 를 가지고 있다.

 

현재는 입력란에 타이핑을 하고 다른 수신자를 선택해도 <Chat> 이 트리의 같은 곳에서 렌더링되기에 입력값이 유지된다.

이것을 고치기 위해 key 를 추가할 수 있다.

<Chat key={to.id} contact={to} />

 

이제 다른 수신자를 선택하면 <Chat> 컴포넌트가 그 트리에 잇는 모든 state 를 포함해 처음부터 다시 생성된다.

React 는 DOM 엘리먼트도 다시 사용하는 대신 새로 만들 것이다.

 

하지만 제거된 컴포넌트의 state 를 보존하고 싶다면?

실제 채팅 앱에선 이전의 수신자를 선택햇을 대 입력했던 값이 복구되는 것을 원할 것이다.

보이지 않는 컴포넌트의 state 를 '살아 있게' 하는 몇 가지 방법이 있다.

  • 현재 채팅만 렌더링하는 대신 모든 채팅을 렌더링하고 CSS 로 안보이게 설정
    • 채팅은 트리에서 사라지지 않을 것이므로 그들의 state 는 유지된다.
    • 이 방법은 간단한 UI 에서 잘 작동하지만, 숨겨진 트리가 크고 많은 DOM 노드를 가지고 있다면 매우 느려질 것
  • state 를 상위로 올려 각 수신자의 임시 메세지를 부모 컴포넌트에 유지
    • 부모가 정보를 가지고 있기에 자식 컴포넌트가 제거되어도 상관이 없다. 가장 일반적인 해법
  • React state 이외의 다른 저장소 이용(예시로, 실수로 페이지를 닫아도 메세지를 유지하고 싶은 경우)
    • localStorage 에 메세지를 저장하고 이를 이용해 Chat 컴포넌트를 초기화

이 중 어떤 방법을 선택하더라도 Alice 와의 채팅은 Bob 과의 채팅 과 개념상 구별되기 때문에

현재 수신자를 기반으로 <Chat> 트리에 key 를 주는 것은 타당하다.

 

 

 

🔗 https://ko.react.dev/learn/sharing-state-between-components

 

컴포넌트 간 State 공유하기 – React

The library for web and native user interfaces

ko.react.dev

 

 

 

728x90
반응형