[React] 비동기적 setState

1 minute read

setState는 함수형 컴포넌트 내에서 상태를 관리하게 위해 사용하는 useState를 통해 반환되는 함수입니다.

기본적으로 setState의 인자값에 변경하고자 하는 상태를 전달하여, 상태를 변경할 수 있습니다.

이 글에서는 setState 함수의 특징과 올바르게 사용하지 않았을 때 발생할 수 있는 오류에 대해서 알아보겠습니다.


setState의 특징

setState 함수는 다음 4가지 특징을 가지고 있습니다.

  • 비동기적으로 동작한다.

  • 연속적으로 호출하면 batch 처리를 한다.

  • setStatestate 객체 뿐 아니라, 새로운 state를 반환하는 함수를 인자로 넘겨줄 수 있다.

  • state는 객체이다.

다음으로 이러한 특징을 고려하지 않고 코딩할 때 발생할 수 있는 문제 예시에 대해서 살펴보겠습니다.


코드 예시

useState를 사용해서 number의 상태를 변경시키는 예시입니다.


const [number, setNumber] = useState(1);

const add = () => setNumber(number + 1);
const subtract = () => setNumber(number - 1);
const multiplyBy2 = () => setNumber(number * 2);
const multiplyBy2AndAddBy1 = () => {
  multiplyBy2();
  add();
};

이 때 multiplyBy2AndAddBy1 함수를 실행시키면, multiplyBy2 후에 add가 실행되는 것이 아니라, 의도한 바와 달리 add만 실행이 됩니다.

이런 현상이 일어나는 이유는 무엇일까요?


문제의 원인

react에서 여러 setState 호출을 만나면, 각 setState에 전달된 객체를 모두 추출한 후 merge하여 단일 객체로 만듭니다.

이후 단일 객체를 사용하여 setState를 수행합니다.

다음 예시코드를 통해 객체가 어떻게 merge되는지 살펴보겠습니다.


const singleObject = Object.assign(
  {},
  objectFromSetState1,
  objectFromSetState2,
  objectFromSetState3
);

이 때 3개의 객체가 동일한 키를 가지고 있다면, Object.assign에 마지막으로 전달된 객체(objectFromSetState3)의 키의 값만 적용됩니다.


해결방법

이 문제를 해결하기 위해서는 setState 함수에 새로운 state를 반환하는 함수를 인자로 전달하면 됩니다.

이렇게 하면 인자로 넘겨 받는 함수들은 Queue에 저장되어 순서대로 실행되게 됩니다.


const [number, setNumber] = useState(1);

const add = () => setNumber(number => number + 1);
const subtract = () => setNumber(number => number - 1);
const multiplyBy2 = () => setNumber(number => number * 2);
const multiplyBy2AndAddBy1 = () => {
  multiplyBy2();
  add();
};

참고자료

[1] 함수형 setState가 리액트의 미래이다

[2] React의 setState() 제대로 사용하기(Blog)

[3] 리액트의 setState() 제대로 사용하기(Youtube)