본문 바로가기
개발/React

Functional update of setState -> state의 변화가 state를 참조하지 않게 됨

by 안뇽! 2022. 8. 13.
반응형

 

 

이렇게 숫자가 혼자서 계속 올라가는 것을 만드려면 어떻게 해야 할까??

 

시도하다보면 useEffect를 쓰는 쪽으로 생각이 흘러간다.

 

처음에는 다음과 같이 작성하였다.

useEffect(() => {
setInterval(() => {
      setCount(count); 
    }, 1000);

  }, [count]);
  
  return (
    <>
      <h1>{count}</h1>
      <button onClick={() => setCount(initialCount)}>Reset</button>
      <button onClick={() => setCount((prevCount) => prevCount - 1)}>-</button>
      <button onClick={() => setCount((prevCount) => prevCount + 1)}>+</button>
    </>
  );

 

그러니 이러한 버그가 나타났다.

 

 

 

그래서 clean-up함수를 추가해주었다.

(clean-up함수가 언마운트시가 아니라, 업데이트시 실행된다는 것을 알 수 있음, 참고)

 

import { useEffect, useState } from "react";

const initialCount = 0;
function FrequentlyChangableDependencyInEffect() {
  const [count, setCount] = useState<number>(initialCount);

  useEffect(() => {
    const id = setInterval(() => {
      setCount(count + 1);
    }, 1000);
    return () => clearInterval(id);
  }, [count]);

  return (
    <>
      <h1>{count}</h1>
      <button onClick={() => setCount(initialCount)}>Reset</button>
      <button onClick={() => setCount((prevCount) => prevCount - 1)}>-</button>
      <button onClick={() => setCount((prevCount) => prevCount + 1)}>+</button>
    </>
  );
}

export default FrequentlyChangableDependencyInEffect;

 

이렇게 하니 버그는 사라졌지만 매 업데이트마다 interval를 초기화하게 된다.

이는 clean-up되기 전에 실행될 기회를 한번밖에 얻지 못하는 것이다.

이는 비효율적일 수 있다.

 

공식문서에서는 다음과 같이

  • 의존성 배열에 빈배열을 넣고,
  • setState안에 callback을 넣으라고 하고 있다.(funtional update)
import { useEffect, useState } from "react";

const initialCount = 0;
function FrequentlyChangableDependencyInEffect() {
  const [count, setCount] = useState<number>(initialCount);

  useEffect(() => {
    const id = setInterval(() => {
      setCount((c) => c + 1); //functional update
    }, 1000);
    return () => clearInterval(id);
  }, []);

  return (
    <>
      <h1>{count}</h1>
      <button onClick={() => setCount(initialCount)}>Reset</button>
      <button onClick={() => setCount((prevCount) => prevCount - 1)}>-</button>
      <button onClick={() => setCount((prevCount) => prevCount + 1)}>+</button>
    </>
  );
}

export default FrequentlyChangableDependencyInEffect;

이렇게 작성하면 현재 state를 참조하지 않고 state를 변경할 수 있게 된다.

 

즉, 현재 state가 업데이트 될 때 마다 clean-up이 실행되는 로직을 피하여 count가 변하여도 clean-up이 실행되지 않는다.(count변화가 state 를 참조하지 않고 변경되기 때문)

 

  • as-is : state업데이트-> clean-up실행 -> state 업데이트 -> clean-up 실행 ...
  • to-be : state업데이트 -> state 업데이트 -> state 업데이트 ..... -> 언마운트시 clean-up실행(state변화가 state를 참조하지 않기 때문에 clean-up으로 이어지지 않음)

더 복잡한 경우(한 state가 다른 state를 의존하는 경우)는 useReducer를 사용하라고 하고 있다.

 

사실 회사에서 useReducer를 사용해봤었는데, 왜 사용했는지 모르고 그냥 기존에 useReducer를 사용한 코드였기 때문에

그대로 사용했었다.

 

이번기회에 useReducer를 왜 쓰는지 알아봐야 할 듯


참고

공식문서

 

 

 

 

반응형