모두들 알다시피 리액트에서 setter함수는 비동기적으로 동작한다.

즉 호출되는 순간 state를 업데이트하고 다음 코드를 실행하는 방식이 아니기 때문에 예기치 못한 오류들에 직면할 수도 있다.

const [count, setCount] = useState(0);

const onAdd = () => {
  setCount(count + 1);
  console.log(count);
}

// 카운트가 0일 때 onAdd가 실행되면 콘솔에 찍히는 카운트값은 1이 아니라 0일수도 있다

위 상황에선 내가 분명 setCount를 통해 count값을 1 늘려줬는데, 콘솔로그에 찍는 코드에서 참조되는 count값은 1 늘어난 값이 아니라 옛날에 쓰던 count값이 되는 문제가 생긴다. 즉 가장 최신의 count값을 참조해야 하지만 못 하게 되는 것이다. setter함수가 비동기적으로 동작하기 때문에..(이 문제는 클래스형 컴포넌트에서는 두 번째 인자로 갱신 이후 실행할 콜백을 넘겨줌으로써 해결할 수 있고 함수형 컴포넌트에선 useEffect를 통해 해결가능했다)

 

또 문제인 것은 setCount가 여러 번, 예를 들어 100번 호출됐다면 100이 늘어나야 하는데 100보다 덜 늘어나는 상황도 생길 수 있다. 각각의 setCount함수들이 참조하는 count값이 가장 최신의 count값이라는 보장이 없기 때문.

 

const [count, setCount] = useState(0);

const onAdd = () => {
  setCount(count + 1);
  setCount(count + 1);
  setCount(count + 1);
  setCount(count + 1);
  setCount(count + 1);
}

// 카운트가 5 늘어날 것이라 예측 가능 but 5만큼 안 늘어났을 수도 있다..

 

이 때 콜백을 써줌으로써 이 문제를 해결 가능하다. 좀 더 풀어서 말하면, 바로 이전의(즉 최신의) state를 사용해 새로운 state를 계산하는 콜백을 setter함수에 전달함으로써 이 문제를 해결할 수 있다. 공식 문서를 참고해보니, 콜백을 사용하는 갱신방법(함수적 갱신)의 경우 바로 이전 state를 사용해서 새로운 state를 계산한다고 한다.

const [count, setCount] = useState(0);

const onAdd = () => {
  setCount(count => count + 1);
  setCount(count => count + 1);
  setCount(count => count + 1);
  setCount(count => count + 1);
  setCount(count => count + 1);
}

 

setCount(count + 1)는 count값을 인자로 전달하므로, 이 때 전달된 값은 나중에 가면 가장 최신의 값이 아닐 수도 있다. 

반면 setCount(count => count + 1)의 경우는 count값이 아니라 실행할 콜백을 인자로 전달하고, 전달된 콜백이 실행되면 그 때서야 그 때의 count값을 참조하므로 이 경우는 가장 최신의 count값을 참조하는 것이다.

+ Recent posts