useState에서 함수형 업데이트는 왜 쓰는 걸까?

로라·2023년 6월 1일
0
post-thumbnail

Udemy 강의에서 이전 상태 값에 의존하여 상태를 업데이트할 때는 함수형 업데이트를 사용하는 것이 안전한 방법이라고 했다. 대다수의 사례에서 함수형 업데이트를 쓰지 않아도 원하는 결과 값으로 업데이트되겠지만, 아닌 경우도 있다고 한다. 함수형 업데이트는 가장 최근 상태 값에 의존하여 업데이트시키기 때문에 더 안전한 방법이라고 한다.

❓ set함수(업데이트 값)과의 차이점은?
❓ 왜 useState가 안전한 방법이라고 하는 걸까?

함수형 업데이트와 nextState 업데이트의 차이점

우선 쉬운 예시에서 둘의 차이점을 발견했다. 하나의 이벤트 내에서 업데이트를 여러 번 할 때이다. +를 누르면 값이 증가하고, -를 누르면 값이 감소하는 Count를 만들어 보았다.

+를 누를 때마다 3씩 증가시키고 싶다고 가정하자.

const Count = () => {
  const [num, setNum] = useState(0);
  const increaseNumHandler = () => {
    setNum(num + 1); 
	setNum(num + 2); // (1)
	console.log(num); // 0 (2)
  };
  const decreaseNumHandler = () => {
    setNum(num - 1);
  };
console.log(num); // 2 (3)
  return (
    <div>
      <button type="button" onClick={increaseNumHandler}>
        +
      </button>
      <span>{num}</span>
      <button type="button" onClick={decreaseNumHandler}>
        -
      </button>
    </div>
  );
};

위의 코드처럼 작성하면 + 버튼을 클릭할 때마다 num 은 2씩만 증가한다. 가장 마지막에 작성된 (1) setNum(num + 2) 에 의해서만 말이다. set함수는 예약어로 업데이트시키기 때문에 상태 값이 바로 업데이트되지 않는다. 그래서 이벤트 내에 있는 두 setNum함수에서 참조하는 num은 모두 현재 상태 값인 0이고(2), 상태가 업데이트됨에 따라 컴포넌트가 리렌더링 되었을 때는 num이 2로 업데이트 된 것이다.(3)
위와 같은 상황처럼 하나의 이벤트 내에서 다수의 상태를 업데이트하고자 할 때, 함수형 업데이트가 유용하다.

const Count = () => {
  const [num, setNum] = useState(0);
  const increaseNumHandler = () => {
    setNum(num=>num + 1); 
	setNum(num=>num + 2);
	console.log(num); // 0 (4)
  };
  const decreaseNumHandler = () => {
    setNum(num - 1);
  };
console.log(num); // 2 
  return (
    <div>
      <button type="button" onClick={increaseNumHandler}>
        +
      </button>
      <span>{num}</span>
      <button type="button" onClick={decreaseNumHandler}>
        -
      </button>
    </div>
  );
};

위와 같이 함수형 업데이트로 작성하면 동기적으로 가장 최신 값에 의존하여 set함수가 동작하기 때문에 3씩 증가한다. (❗ 그래도 여전히 console.log(num) 은 0이다.(4)) 함수형 업데이트는 가장 최신의 예약어까지도 의존하여 동작을 실행시킨다고 이해했다. 함수형 업데이트가 상태값을 바로 업데이트 시키는 것은 아니다.

문제

CountA와 CountB에서 +를 누르면 각각 몇 씩 증가할까?

const CountA = () => {
  const [num, setNum] = useState(0);
  const increaseNumHandler = () => {
    setNum(num + 2); // (2)
	setNum(num=>num + 3); // (1)
  }; 
  return (
    <div>
      <button type="button" onClick={increaseNumHandler}>
        +
      </button>
      <span>{num}</span>
    </div>
  );
};
const CountB = () => {
  const [num, setNum] = useState(0);
  const increaseNumHandler = () => {
    setNum(num=>num + 2); // (3)
	setNum(num + 3); // (4)
  }; 
  return (
    <div>
      <button type="button" onClick={increaseNumHandler}>
        +
      </button>
      <span>{num}</span>
    </div>
  );
};

CountA는 5씩, CountB는 3씩 증가한다.

해설

CountA의 함수형 업데이트 (1) setNum(num=>num + 3); 은 가장 최신의 예약어까지도 의존하기 때문에 (2)에서 업데이트 예약한 2의 값에 의존하여 결과적으로 5씩 증가한다.
CountB는 (3)에서 2로 set함수가 동작하지만 (4) 코드에서 현재 상태값인 0을 의존하기 때문에 최종적으로 3씩 증가한다.

함수형 업데이트가 왜 더 안전한 방법일까?

이전 상태에 의존하여 업데이트 시킬 때 함수형 업데이트가 더 권장된다.
useState는 바로 값을 업데이트 시키기 않기 때문에 이전 값만 의존하는 것이 아니라 최신 예약어 까지도 포함하여 의존하는 함수형 업데이트가 더 안전하다고 하는 것이다. 공식문서에서는 코드가 장황해지는 것보다(예시처럼 num + 1 보다, num ⇒ num+1은 더 긴 코드임) 상태값의 일관성이 더 중요하다고 생각한다면 함수형 업데이트를 사용하는 것이 명백하다고 적혀있다.

마무리

필수는 아니지만 일관되고 안전한 상태관리를 위해 이전 상태값에 의존하여 업데이트시킬 때는 함수형 업데이트를 사용하는 습관을 들여야겠다.
공식문서에서 상태 변수 자체에 접근하는 것이 어려운 상황에서도 함수형 업데이트가 효율적이라고 했다. 당장 간단한 Count 예시에서 useCallback()으로 최적화도 시켜보고 다른 상태도 만들어보았지만 차이를 발견하지는 못했다.
공부를 하면서 하나의 이벤트 내에서 상태를 여러번 업데이트 시키는 경우가 있을까? 하는 물음이 계속 떠올랐다. 만든 Count예시에서도 3씩 증가시키고 싶으면 처음부터 3을 증가시키고 되기 때문이다..😅
앞으로 이전 값에 의존하여 업데이트 시킬 때 두가지 방법을 모두 사용해보고 다른 결과값이 나온다면 그때의 문제 상황을 분석해보고 공부해봐야겠다!

profile
어렵게 접한 것을 다른 사람은 쉽게 접하도록 기록합니다.

0개의 댓글