state 보존 및 재설정
state는 컴포넌트 간에 격리된다. React는 UI 트리에서 어떤 컴포넌트가 어떤 state에 속하는지를 추적한다. React는 state를 언제 보존하고 언제 초기화할지를 제어할 수 있다.
학습 내용
- React가 컴포넌트 구조를 “보는” 방법
- React가 state를 유지하거나 재설정하도록 선택할 때
- React가 컴포넌트의 state를 재설정하도록 강제하는 방법
- key와 type이 state 보존 여부에 영향을 미치는 방법
UI 트리
- 브라우저는 UI를 모델링하기 위해 "여러 트리 구조"를 사용한다.
- DOM은 HTML element
- CSSOM은 CSS
- Accessibility 트리도 있다!
=> 이처럼, React도 트리 구조를 사용하여 사용자의 UI를 관리하고 모델링한다!
- React이 JSX(또는 TSX)로부터 UI 트리를 만든다. 그런 다음 React DOM은 해당 UI 트리와 일치하도록 브라우저 DOM을 업데이트한다.
state는 트리의 한 위치에 묶인다.
- 컴포넌트에 state를 부여할 때
- 컴포넌트 내부에 state가 존재한다고 생각할 수도 있다.
- 그러나, 실제로 state는 React 내부에서 유지된다.
- React는 UI 트리에서 컴포넌트의 위치에 따라 보유하고 있는 각 state를 올바른 컴포넌트와 연결한다.
동일한 위치의 동일한 컴포넌트는 state를 유지한다.
- React는 컴포넌트가 UI 트리의 해당 위치에서 렌더링되는 동안 컴포넌트의 state를 유지한다.
- 즉, 컴포넌트가 제거되거나 같은 위치에 다른 컴포넌트가 렌더링되면 React는 해당 컴포넌트의 state를 삭제한다.
<div>{isFancy ? (
<Counter isFancy={true} />
) : (
<Counter isFancy={false} />
)}</div>
- isFancy가 true이든 false이든,
가 렌더링되는 것은 동일하기 때문에 Counter의 state는 재설정되지 않는다! - UI 트리에서의 위치가 같기 때문에 React의 관점에서는 같은 컴포넌트이다.
동일한 위치의 다른 컴포넌트는 state를 초기화한다.
<div>
{isPaused ? (
<p>See you later!</p>
) : (
<Counter />
)}
</div>
- UI 트리 상에서 같은 위치에 다른 컴포넌트가 전환되므로, React는 Counter를 제거하고 그 state를 소멸시킨다.
- 또한, 같은 위치에 다른 컴포넌트를 렌더링하면 전체 하위 트리의 state가 재설정된다.
<div>
{isFancy ? (
<div>
<Counter isFancy={true} />
</div>
) : (
<section>
<Counter isFancy={false} />
</section>
)}</div>
- 위 컴포넌트가 서로 전환되면 state가 재설정된다.
가 렌더링되더라도 div의 첫 번째 자식이 div에서 section으로 변경되었기 때문에, 자식 div가 DOM에서 제거되면 하위 전체 트리도 함께 제거된다.
컴포넌트를 중첩하지 말자.
import { useState } from 'react';
export default function MyComponent() {
const [counter, setCounter] = useState(0);
function MyTextField() {
const [text, setText] = useState('');
return (
<input
value={text}
onChange={e => setText(e.target.value)}
/>
);
}
return (
<>
<MyTextField />
<button onClick={() => {
setCounter(counter + 1)
}}>Clicked {counter} times</button>
</>
);
}
- 이 컴포넌트는 버튼을 입력할 때마다 state가 사라진다.
- 이는 MyComponent를 렌더링할 때마다 다른 MyTextField 함수가 생성되기 때문이다.
- 이 문제를 방지하려면 항상 컴포넌트 함수를 최상위 수준에서 선언해야 한다!
동일한 위치에서 state 재설정하기
- 기본적으로 React는 컴포넌트가 같은 위치에 있는 동안 state를 보존한다.
- 그러나 가끔 state를 의도적으로 리셋하고 싶을 때가 있다.
- 컴포넌트를 같은 위치에서 전환할 때, state를 재설정하는 방법은 두 가지가 있다!
- 컴포넌트를 다른 위치에서 렌더링하기
{isPlayerA &&
<Counter person="Taylor" />
}
{!isPlayerA &&
<Counter person="Sarah" />
}
- 두 카운터를 독립적으로 만들기 위해서는 두 개의 다른 위치에 렌더링하면 된다.
- 각 카운터의 state는 DOM에서 제거될 때마다 소멸된다.
- 이 솔루션은 같은 위치에 몇 개의 독립적인 컴포넌트만 렌더링할 때 편리하다.
- key로 state 재설정하기
- 반복문을 사용해서 목록을 렌더링할 때처럼 key를 사용하면 된다.
- 컴포넌트마다 다른 key를 지정하면 React에게 다른 컴포넌트 임을 알릴 수 있다.
- key를 지정하면 React가 부모 내순서가 아닌 key 자체를 위치의 일부로 사용하도록 지시한다.
- 따라서 JSX에서 같은 위치에 렌더링하더라도 React의 관점에서 보면 서로 다른 카운터이다.
- 단, key는 전역으로 고유하지는 않다! key는 부모 내에서의 위치만 지정한다.
제거된 컴포넌트에 대한 state 보존
- 제거된 컴포넌트의 state를 보존했다가 복구하고 싶을 때가 있다.
- 다음의 방법을 사용해보자.
- 현재 컴포넌트만 "렌더링"하는 것이 아니라, 모든 컴포넌트를 렌더링하되 다른 모든 컴포넌트를 css로 숨길 수 있다. 컴포넌트는 트리에서 제거되지 않으므로 로컬 state가 유지된다! 간단하지만 숨겨진 트리가 크고 많은 DOM 노드를 포함한 경우 속도가 매우 느려질 수 있다.
- 부모 컴포넌트에서 각 자식의 state를 끌어올려서 보관할 수 있다. 가장 일반적인 솔루션이다.
- React state 외에 다른 소스를 사용할 수 있다. localStorage 에 값을 쓰고 읽어서 state를 초기화할 수 있다!
Recap
- React는 동일한 컴포넌트가 동일한 위치에서 렌더링되는 한 state를 유지한다.
- state는 JSX 태그에 보관되지 않습니다. JSX를 넣은 트리 위치와 연관되어 있다.
- 하위 트리에 다른 key를 지정하여 강제로 state를 재설정할 수 있다.
- 컴포넌트 정의를 중첩하지 마세요. 실수로 state가 초기화될 수 있다.
'프로젝트 > 정글 FE 스터디' 카테고리의 다른 글
[React] Passing Data Deeply with Context (1) | 2023.10.16 |
---|---|
[FE] Deep Dive - 44장 REST API (0) | 2023.10.16 |
[React] Choosing the State Structure (0) | 2023.10.16 |
[React] State as a Snapshot (0) | 2023.09.19 |
[React] Render and Commit (0) | 2023.09.18 |