본문 바로가기

React/Hooks

React Hooks의 State 동작 원리

 

리액트 컴포넌트는 state가 변할 때마다 다시 실행된다.

잦은 state 변화가 발생하는 경우 내부적으로 어떻게 작동하는지를 알고 있지 못한다면

심각한 성능 이슈를 초래할 수 있다.

한편 2021년 기준으로 함수형 컴포넌트가 메인이 되면서 state 관리는 hooks가 맡아서 하게 됐다.

여기서는 주요 hooks에서 제공하는 state의 변화에 따라 리액트가 어떻게 반응하는지를 다뤄보겠다.

 

State 변화에 따라 어떻게 동작하는가?

리액트의 function 컴포넌트는 최초에 한 번 실행이 되고 나서

초기값으로 설정해 놓은 state를 기억하고 있는다.

 

// 0을 초기값으로 한다.
const [state, setState] = useState(0);

 

버튼 클릭 이벤트 등으로 setState가 호출되면   

다시 function 컴포넌트가 실행되고 Virtual DOM을 리턴한다.

이전에 리턴했던 Virtual DOM과 비교해서 state 값이 달라졌다면

달라진 부분에 해당하는 실제 DOM만을 업데이트 한다.

 

예를 들어보자.

아래 예제는 버튼을 클릭할 때마다 number state가 달라지고

Home 컴포넌트가 호출된다.

 

const Home = () => {
  console.log('Home 컴포넌트 호출');
  const [number, setNumber] = useState(0);

  const handleClick = () => {
    setNumber(number + 1);
  };

  return (
    <div>
      <p>{number}</p>
      <Button onClick={handleClick}>Plus</Button>
    </div>
  );
};

 

이 과정에서 return 이하의 Virtual DOM이 리렌더링되는데 

(When you render a JSX element, every single virtual DOM object gets updated.)

리액트는 이전의 Virtual DOM과 차이 있는 부분을 캐치해서 

해당 부분의 실제 DOM만을 업데이트한다.

(차이점을 적은 리소스로 파악하기 위해 실제 DOM이 아닌 Virtual DOM을 활용하는 것이다.)

위 예제에서는 <p>{number}</p>가 이에 해당한다.

크롬 개발도구의 요소(Element) 탭을 보면 p 엘리먼트만 업데이트되는 것을 확인할 수 있다.

 

리액트가 성능이 좋다는 말은 이렇게 실제 DOM 중 변하는 부분만을 업데이트하기 때문에 나오는 말이지 (페이지 전체를 바꾸는 게 아니고)

state가 바뀔 때마다  컴포넌트의 모든 로직이 실행되므로 자바스크립트로 직접 엘리먼트를 수정하는 것보다 효율적이지는 못하다.

 

다만 Virtual DOM을 이용해서 나름 적은 연산으로 손쉽게 실제 DOM의 업데이트 될 부분을 찾아낼 수 있기 때문에 코딩이 쉬워진다는 장점이 있다.

 

참고 : codecademy.com/articles/react-virtual-dom

 

useCallback과 useMemo를 사용하는 이유

만약 컴포넌트 내부에 연산이 많이 필요한 로직 또는 함수가 포함돼 있다면

state가 변경될 때마다 Virtual DOM의 리렌더링을 위해서 실행될 것이므로 성능 이슈가 발생한다.

이를 막기 위해서 useCallback과 useMemo를 사용해 복잡한 로직을 스킵하고 이전에 연산했던 결과를 써먹는다.

자세한 사용 방법은 아래 링크의 매뉴얼을 참고하자.

ko.reactjs.org/docs/hooks-reference.html#usecallback

 

(React memo를 사용하는 경우 prop으로 내려주는 변수를 동일하게 유지하고 싶을 때도 useCallback, useMemo를 사용한다.) 

 

Redux의 useSelector

Redux에서 custom hook으로 제공하는 useSelector를 이용하면 redux로 관리하는 state를 가져올 수 있다.

useSelector는 function 컴포넌트가 렌더링될 때와 action이 dispatch될 때 작동한다.

action이 dispatch되면 useSelector는 이전의 결과값과 비교를 하고 다를 때에만 강제로 리렌더링한다.

여기서 비교하는 결과값은 useSelector가 return하는 값이다.

하나의 컴포넌트에서 여러 useSelector가 사용된다고 해도 한 번만 리렌더링된다.

react-redux.js.org/api/hooks#useselector

 

깃허브 : github.com/socratone/react-rerendering-test