React에서는 컴포넌트 안의 상태 값을 관리하기 위해 useState
함수를 사용한다. useState
는 const [state, setState] = useState();
와 같이 state
라는 변수와 setState()
라는 해당 값을 변경하는 함수로 구성된다.
평소처럼 useState
를 사용하던 중, setState()
가 생각한대로 작동하지 않는 문제를 발견하였다. 하나의 setState()
를 한 번에 여러번 요청하면 마지막에 요청된 setState()
만 수행하는 것처럼 작동하는 것이다.
왜 이런 문제가 발생하는 것일까?
결론적으로 말하자면 setState()
호출은 비동기적으로 이루어지기 때문이다.
import { useState } from "react";
function App() {
const [number, setNumber] = useState(0);
const plusNumber = () => {
setNumber(number + 1);
setNumber(number + 1);
setNumber(number + 1);
}
return (
<div>
<p>{number}</p>
<button onClick={plusNumber}>+3</button>
</div>
);
}
export default App;
위의 코드를 보면 +3버튼을 눌렀을 때, number가 3씩 증가할 것이라고 예상된다.
하지만 예상과는 다르게 1씩 증가하는 것을 확인할 수 있다.
setState()
는 컴포넌트의 state
객체에 대한 업데이트를 실행한다. state
가 변경되면, 컴포넌트는 리렌더링된다.
또한, 모든 컴포넌트가 자신의 이벤트 핸들러에서 setState()
를 호출할 때까지 React는 리렌더링을 하지 않고 내부적으로 기다리고 있다. 이를 통해 불필요한 렌더링을 방지하면서 성능을 향상시킨다.
React는 상태 값을 업데이트 할 때 모든 요청에 따라 바로바로 rerender가 되는것이 아닌 변경사항을 모아서 한번에 일괄 처리(batch update)를 하고, 이 일괄 업데이트를 통해 컴포넌트의 렌더링 횟수를 최소화하는 것이다.
따라서 위의 코드에서 1씩 증가하는 것처럼 보이는 결과가 나타난 것은, setNumber()
에 number+1
이 수행된 결과 값이 아닌 number
값이 들어가기 때문이다. number
가 0일 때 버튼을 누르면 setNumber()
에 각각 0, 1, 2가 아니라 모두 0이 들어가므로 결과적으로 +1이 되서 마지막 함수만 수행된 것처럼 보이는 것이다.
setState()
가 가장 최신의 state
값을 사용하는 방법으로는 setState()
에 객체 대신 함수를 전달하는 것이 있다.
updater 함수를 전달하면 updater 함수 안에서 이전 state
값에 접근할 수 있다. setState()
호출은 일괄적으로 처리되기 때문에 여러 업데이트 사항이 충돌 없이 차례대로 반영되도록 한다.
const plusNumber = () => {
setNumber(num => num + 1);
setNumber(num => num + 1);
setNumber(num => num + 1);
}
함수로 넘겨주니 제대로 동작한다!
📕 참조