본문 바로가기
React/Documentation

리액트 공식 문서 번역-18. 컴포넌트간 상태 공유하기

by 똘이토스 2023. 7. 13.

때로는 두 컴포넌트의 상태가 항상 함께 변경되기를 원할 때가 있습니다. 이를 위해서는 두 컴포넌트에서 상태를 제거하고 가장 가까운 공통 부모 컴포넌트로 이동시킨 다음, props를 통해 하위 컴포넌트로 전달합니다. 이것이 상태를 들어 올리는(lifting state up) 방법입니다. 이 방법은 React 코드를 작성할 때 가장 일반적인 작업 중 하나입니다.

 

상태 끌어올리기 예제

예시로, 부모 컴포넌트인 Accordion은 두 개의 별개의 Panel을 렌더링합니다

 

  • Accordion
    • Panel
    • Panel

 

각각의 Panel 컴포넌트는 해당 내용이 보이는지를 결정하는 boolean isActive 상태를 가집니다.

 

두 패널에 대한 Show 버튼을 눌러보세요.

 

https://codesandbox.io/s/60d400?file=/App.js&utm_medium=sandpack

 

agitated-rubin-60d400 - CodeSandbox

agitated-rubin-60d400 using react, react-dom, react-scripts

codesandbox.io

 

한 패널의 버튼을 눌러도 다른 패널에 영향을 미치지 않는 것을 확인할 수 있습니다 - 이들은 독립적입니다.

 

하지만 이제 한 번에 하나의 패널만 확장되도록 변경하고 싶다고 가정해 봅시다. 이 설계에서 두 번째 패널을 확장하면 첫 번째 패널이 축소되어야 합니다. 이를 어떻게 할 수 있을까요?

 

이 두 패널을 조정하려면, 세 가지 단계로 공통 부모 컴포넌트에 상태를 "위로 올려야"합니다

 

  1. 자식 컴포넌트에서 상태를 제거합니다.
  2. 공통 부모로부터 하드코딩된 데이터를 전달합니다.
  3. 공통 부모에 상태를 추가하고, 이벤트 핸들러와 함께 하향식으로 전달합니다.

 

이렇게 하면 Accordion 컴포넌트가 두 패널을 조정하고 한 번에 하나의 패널만 확장할 수 있게 됩니다.

 

Step 1: 자식 컴포넌트에서 상태를 제거

당신은 Panel 컴포넌트의 isActive 상태를 부모 컴포넌트에게 제어를 넘길 것입니다. 이는 부모 컴포넌트가 isActivePanel에게 props으로 전달하는 것을 의미합니다. Panel 컴포넌트에서 다음 줄을 제거하여 시작하세요

 

const [isActive, setIsActive] = useState(false);

 

대신, isActivePanelprops 목록에 추가하세요

 

function Panel({ title, children, isActive }) {

 

이제 Panel의 부모 컴포넌트는 isActiveprop으로 전달함으로써 isActive를 제어할 수 있습니다. 반대로, Panel 컴포넌트는 이제 isActive의 값을 제어할 수 없습니다. 이젠 부모 컴포넌트에게 달려있습니다!

 

Step 2: 공통 부모로부터 하드코딩된 데이터를 전달

state를 올리려면, 함께 변경하고자 하는 두 자식 컴포넌트의 가장 가까운 공통 부모 컴포넌트를 찾아야 합니다

 

  • Accordion (가장 가까운 공통 부모)
    • Panel
    • Panel

 

이 예제에서는 Accordion 컴포넌트가 그것입니다. Accordion 컴포넌트가 두 패널의 props를 제어할 수 있으므로 현재 활성화된 패널을 나타내는 "진실의 원천"이 될 것입니다. Accordion 컴포넌트에서 true와 같은 하드코딩된 isActive 값을 두 패널에게 전달하도록 만듭니다.

 

https://codesandbox.io/s/g47pds?file=/App.js&utm_medium=sandpack

 

clever-james-g47pds - CodeSandbox

clever-james-g47pds using react, react-dom, react-scripts

codesandbox.io

 

Accordion 컴포넌트에서 하드코딩된 isActive 값들을 수정해보고 화면에서 결과를 확인해보세요.

 

Step 3: 공통 부모에 상태를 추가

상태를 끌어올리면서 상태 변수의 성격이 종종 바뀝니다.

 

이 경우에는 한 번에 하나의 패널만 활성화되어야 합니다. 따라서 Accordion 공통 부모 컴포넌트는 활성화된 패널의 인덱스를 추적해야 합니다. 불린 값 대신, 상태 변수로 활성화된 Panel의 인덱스를 사용할 수 있습니다. 예를 들면

 

const [activeIndex, setActiveIndex] = useState(0);

 

Show 버튼을 누르면 첫 번째 패널이 활성화되고, activeIndex 값이 0이 되며, 두 번째 패널이 활성화되면 activeIndex 값이 1이 됩니다.

패널에서 "Show" 버튼을 클릭하면 Accordion 내에서 정의된 activeIndex 상태를 직접 변경할 수 없습니다. 따라서 Accordion은 이벤트 핸들러를 prop으로 전달하여 Panel 컴포넌트가 상태를 변경할 수 있도록 명시적으로 허용해야 합니다.

 

<>
  <Panel
    isActive={activeIndex === 0}
    onShow={() => setActiveIndex(0)}
  >
    ...
  </Panel>
  <Panel
    isActive={activeIndex === 1}
    onShow={() => setActiveIndex(1)}
  >
    ...
  </Panel>
</>

 

Panel 내부의 <button>은 이제 클릭 이벤트 핸들러로 onShow prop을 사용합니다

 

https://codesandbox.io/s/1jkdgm?file=/App.js&utm_medium=sandpack

 

hopeful-raman-1jkdgm - CodeSandbox

hopeful-raman-1jkdgm using react, react-dom, react-scripts

codesandbox.io

 

이로써 state를 공통 부모 컴포넌트로 올리는 작업이 완료되었습니다! 공통 부모 컴포넌트로 state를 이동함으로써 두 개의 패널을 조정할 수 있게 되었습니다. 두 개의 "표시 여부" 플래그 대신에 활성 패널이 하나만 있도록 활성 인덱스를 사용했습니다. 그리고 자식 컴포넌트에 이벤트 핸들러를 전달하여 자식이 부모의 상태를 변경할 수 있도록 했습니다.

 

제어 및 비제어 컴포넌트

더보기

로컬 상태를 가진 컴포넌트를 "비제어(uncontrolled)" 컴포넌트라고 부르는 것이 일반적입니다. 예를 들어, isActive 상태 변수를 가진 원래의 Panel 컴포넌트는 부모가 패널이 활성화되었는지 여부를 영향을 주지 못하므로 비제어 상태입니다.

 

반면, 중요한 정보가 로컬 상태 대신 프롭에 의해 주도되는 경우 "제어(controlled)"된 컴포넌트라고 할 수 있습니다. 이렇게 하면 부모 컴포넌트가 그 동작을 완전히 지정할 수 있습니다. isActive prop을 가진 최종 Panel 컴포넌트는 Accordion 컴포넌트에 의해 제어됩니다.

 

비제어 컴포넌트는 구성하는 데 덜 많은 구성이 필요하므로 부모 컴포넌트 내에서 사용하기가 더 쉽습니다. 그러나 비제어 컴포넌트는 함께 조정할 때 유연성이 떨어집니다. 제어된 컴포넌트는 최대한 유연하지만 부모 컴포넌트가 프롭으로 완전히 구성해야 합니다.

 

실제로 "제어(controlled)" 및 "비제어(uncontrolled)"는 엄격한 기술적인 용어는 아닙니다. 각 컴포넌트는 일반적으로 로컬 상태와 프롭의 혼합물을 가지고 있습니다. 그러나 이것은 컴포넌트가 어떻게 설계되었는지와 어떤 기능을 제공하는지에 대해 이야기하기에 유용한 방법입니다.

 

컴포넌트를 작성할 때, 어떤 정보가 프롭(프롭을 통해 제어)으로 제어되어야 하고 어떤 정보가 로컬 상태(비제어)로 유지되어야 하는지를 고려하세요. 그러나 나중에 마음을 바꿀 수 있고 리팩토링할 수 있습니다.

 

각 상태에 대한 정보 단일 출처

React 애플리케이션에서 여러 컴포넌트에는 각자의 state가 있습니다. 입력 필드와 같이 리프(leaf) 컴포넌트(트리의 맨 아래 컴포넌트)에 가까운 상태도 있고, 다른 상태는 앱의 상단에 가까운 곳에 있을 수 있습니다. 예를 들어, 클라이언트 측 라우팅 라이브러리도 현재 라우트를 React state에 저장하고, props로 전달하여 구현됩니다!

 

각 고유한 상태에 대해 "소유"하는 컴포넌트를 선택해야 합니다. 이 원칙은 "정보 단일 출처(single source of truth)"라고도 합니다. 이는 모든 상태가 한 곳에 모여있는 것을 의미하지는 않지만, 각 상태의 정보를 보유하는 특정 컴포넌트가 있다는 것을 의미합니다. 공유 상태를 컴포넌트 간에 중복으로 복사하는 것이 아니라, 공통으로 사용하는 부모 컴포넌트에 상태를 전달하고 필요한 자식 컴포넌트에 전달합니다.

 

앱을 개발하면서 상태가 다운되거나 다시 올라가는 것이 일반적입니다. 이 모든 것은 프로세스의 일부입니다!

 

더 많은 컴포넌트와 함께 실제로 어떤 느낌인지 보려면, "Thinking in React"를 읽어보세요.

 

요약

  • 두 개의 컴포넌트를 조정하려면, 공통 부모로 상태를 이동하십시오.
  • 그런 다음 공통 부모에서 정보를 props를 통해 하위 컴포넌트로 전달하십시오.
  • 마지막으로, 이벤트 핸들러를 하위 컴포넌트로 전달하여 자식이 부모의 상태를 변경할 수 있도록 합니다.
  • 컴포넌트를 "제어" (props에 의해 구동) 또는 "제어하지 않음" (상태에 의해 구동)으로 간주하는 것이 유용합니다.

 

2023.07.14 - [React/Documentation] - 리액트 공식 문서 번역-19. 상태 보존 및 초기화

 

리액트 공식 문서 번역-19. 상태 보존 및 초기화

컴포넌트 간의 상태는 고립되어 있습니다. React는 UI 트리에서 컴포넌트의 위치를 기준으로 어떤 상태가 어떤 컴포넌트에 속하는지 추적합니다. 리렌더링 사이에 상태를 언제 보존하고 언제 초

ddor2.tistory.com