공식문서를 크게 크게 한 챕터씩 정리하다가
3번째 챕터인 Managing State 부분부터는 한 강의 내용이 슬슬 깊어지는 듯 하여
한 강씩 정리하려고 한다.
Preserving and Resetting State
본 챕터의 가장 주된 내용은 어떻게 State
를 보존하고, 새로 셋팅할 것이냐에 대한 내용이다.
State
는 누가 관리하는가import { useState } from 'react';
export default function App() {
return <Counter />
}
function Counter() {
const [count, setCount] = useState(0);
return (
<>
<button
onClick={() => {
setCount(count + 1);
}}
>
+
</button>
<p>{count}</p>
<button
onClick={() => {
setCount(count - 1);
}}
>
-
</button>
</>
);
}
다음처럼 Counter
컴포넌트를 만들고 + , -
버튼을 누르면 setCount
가 실행되며
Counter
컴포넌트가 재호출 될 것이다.
재호출되면 setCount
로 인해 변경된 count
값으로 새롭게 렌더링 되는 것이다.
그럼 의문이 든다.
const [count, setCount] = useState(0); // count 는 const 인데 어떻게 재할당이 되지 ?
컴포넌트의 상태는 어떻게 변하는 것일까 ?
state
는 컴포넌트가 관리할까 ?import { useState } from 'react';
import './App.css';
export default function App() {
const [isRender, setIsRender] = useState(true);
return (
<div style={{ display: 'flex', gap: '10px' }}>
<Counter />
{isRender && <Counter />}
<label>
<input
checked={isRender}
onClick={() => {
setIsRender(!isRender);
}}
type='checkbox'
/>
Render Second Counter
</label>
</div>
);
}
function Counter() {
const [count, setCount] = useState(0);
return (
<div className='counter'>
<button
onClick={() => {
setCount(count + 1);
}}
>
+
</button>
<p>{count}</p>
<button
onClick={() => {
setCount(count - 1);
}}
>
-
</button>
</div>
);
}
App
컴포넌트는 각자 local state
를 갖는 Counter
컴포넌트를 호출한다.
처음에는 두 개의 Counter
컴포넌트를 렌더링 하고 체크박스를 취소하면 두 번째 컴포넌트는 렌더링 되지 않는다.
이 때 의문이 든다.
만약 Counter
컴포넌트가 개별적으로 local state
를 가지고 있으니
사라졌다가 다시 렌더링 되더라도 본인의 local state
를 기억하고 있지 않을까 ?
어 ~ 아니야 ~
이로서 컴포넌트의 state
를 기억하는 주체는 해당 state
가 정의된 컴포넌트가 아님을 알았다.
state
를 기억할까 ?컴포넌트의 state
는 React
가 기억한다.
리액트는 re-rendering
이 트리거 되는 시기인, state
가 변경되는 시기를 기점으로
변경되기 전, 후 두 가지의 virtual DOM
을 생성한 후
두 Virtual DOM
을 비교한다고 하였다.
비교 할 때 리액트는 여러가지를 비교하지만 이번에 다룰 내용들만 다룬다면
해당 위치에 해당 컴포넌트가 여전히 존재하는가 ?
를 기준으로 state
를 기억한다.
state
란 시시각각 변하는 데이터를 의미한다.
이 때 변함과 동시에 이전과 동일한 위치에 동일한 컴포넌트라면
이전의 state
를 그대로 사용해기 위해 useState
를 호출하여도 이전 state
를 사용하지만
새로 생성된 컴포넌트라면 새로운 state
가 필요하다고 생각하여
useState
를 다시 호출되하는 것이다.
위 예시에서는 리액트 공식문서에서 제공하는 이미지처럼
Virtual DOM
의 두 번째 컴포넌트가 제거되었다가 나타나는 것이기 때문에
새로운 컴포넌트라고 인식하여 useState
에서 새로운 값을 다시 호출한다.
이를 좀 더 엄밀하게 말하면
state
는 컴포넌트 단에서 기억하고 관리하는 것이 아니라
리액트가 컴포넌트의 생명주기에 맞춰 기억하고 관리한다.
위 예시에서는 두 번째 컴포넌트의 생명주기가 종료되었기 때문에 기억하고 있던 state
는 잊어지고
새로운 두 번째 컴포넌트의 생명주기가 시작되었기 때문에 새로운 state
를 생성해낸다.
컴포넌트의 생명주기 ?
컴포넌트가Mount
되느냐Update
되느냐UnMount
되느냐에 따라 컴포넌트의 생명주기를 표현한다.
마운트란Virtual DOM
의 컴포넌트가Actual DOM
에 나타나는 행위를 의미하는데
이는 클래스형 리액트에서 중요하게 다뤘던 듯 싶다.
현재 나는 함수형 리액트만 다뤄봤기 때문에 자세한 내용은 아직 모르지만
위 예시에서 두 번째 컴포넌트가 제거되었다가 새롭게 나타난 것은Unmount
되었다가 다시Mount
된 것일 것이다.
위의 예시를 보다보면 , (또 공식 문서를 읽다보면)
그럼 컴포넌트의 생명 주기는 노드가 존재하는 레벨에서의 (형제 노드들 중에서) 위치로 결정되는걸까 ?
하는 생각이 들 수 있다.
사실 내가 그랬다.
하지만 컴포넌트의 생명주기는 위치와는 상관이 없다.
import { useState } from 'react';
import './App.css';
export default function App() {
const [counters, setCounters] = useState([
{
id: 0,
body: <Counter color='#038C7F' />,
},
{
id: 1,
body: <Counter color='#ADD9D1' />,
},
{
id: 2,
body: <Counter color='#D95252' />,
},
]);
// counters 배열을 랜덤하게 바꾸는 메소드
function handleOnclick() {
const copied = [...counters];
for (let i = copied.length - 1; i > 0; i -= 1) {
const j = Math.floor(Math.random() * (i + 1));
[copied[i], copied[j]] = [copied[j], copied[i]];
}
setCounters(copied);
}
return (
<>
<div className='wrapper'>
{counters.map((component) => (
<div key={component.id}>{component.body}</div>
))}
<button onClick={handleOnclick}>Shuffle</button>
</div>
</>
);
}
function Counter({ color }) {
const [num, setNum] = useState(0);
return (
<div className='container' style={{ backgroundColor: color }}>
<p>{num}</p>
<button
onClick={() => {
setNum(num + 1);
}}
>
Increase Number
</button>
</div>
);
}
동일한 형제 노드에 존재하는 컴포넌트끼리의 위치를 변경하더라도 state
는 여전히 기억되고 있는 모습을 볼 수 있다.
state
를 잊는다는 것은, 해당 컴포넌트가 Actual DOM
에서 Unmount
될 때 뿐이라는 것을 기억하자
위 예시의 컴포넌트들은 본인의 레벨에서 위치만 변경 될 뿐, 동일한 key
값들을 가진 같은 타입의 컴포넌트 (Counter
) 로 여전히 존재하기 때문에 리액트는 state
를 기억하고 제공한다.
이후에는 컴포넌트의 state
를 잊도록 하는 법에 대한 이야기들이 나온다.
방법으로는 식별자인 key
를 다르게 이용하거나, 애초에 잊을 때 다른 컴포넌트로 감싸거나 수정하여 unmount
시키는 방법들에 대한 이야기가 나온다.