React Learn - 컴포넌트의 state 는 누가 기억할까

ChoiYongHyeun·2024년 2월 23일
1

리액트

목록 보기
6/31
post-thumbnail

공식문서를 크게 크게 한 챕터씩 정리하다가

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 를 기억할까 ?

컴포넌트의 stateReact 가 기억한다.

리액트는 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 시키는 방법들에 대한 이야기가 나온다.

profile
빨리 가는 유일한 방법은 제대로 가는 것이다

0개의 댓글