setState를 사용하는 또다른 방식으로 이해하면 좋겠다.
함수형 업데이트는 setState의 인자로 수정할 값이 아니라, 함수를 넣는 방식을 말한다.
// src/App.js
import { useState } from "react";
const App = () => {
const [number, setNumber] = useState(0);
return (
<div>
{/* 버튼을 누르면 1씩 플러스된다. */}
<div>{number}</div>
<button
onClick={() => {
setNumber(number + 1); // 첫번째 줄
setNumber(number + 1); // 두번쨰 줄
setNumber(number + 1); // 세번째 줄
}}
>
버튼
</button>
</div>
);
}
export default App;
이런 방식으로 setNumber 함수를 세번 실행시키면 number값이 세번 변하게 될까? 정답은 '아니다'이다. 각 행의 setNumber함수가 각각 실행되는 것이 아니라, batch(배치)로 처리되기 때문이다. 그래서 백번을 명령해도 한번만 실행된다.
의도하던 동작대로 실행을 시키려면 다음고 같이 실행해야 한다.
// src/App.js
import { useState } from "react";
const App = () => {
const [number, setNumber] = useState(0);
return (
<div>
{/* 버튼을 누르면 3씩 플러스 된다. */}
<div>{number}</div>
<button
onClick={() => {
setNumber((previousState) => previousState + 1);
setNumber((previousState) => previousState + 1);
setNumber((previousState) => previousState + 1);
}}
>
버튼
</button>
</div>
);
}
export default App;
처음에 "setNumber함수 내부의 previousState라는 값이 어디서 뚝 떨어진거지?"하고 생각했었는데, setNumber함수 내부에서 실행되는 함수의 인자는 기본적으로 설정된 초기값을 받아온다고 한다. 그래서 set함수에 대한 콜백함수로 저렇게 설정해주어야 초기값에 대한 변경을 세번 인식하고 세번 적용하게 된다고 한다.
공식문서)
리액트는 성능을 위해 setState()를 단일 업데이트(batch update)로 한꺼번에 처리할 수 있습니다.
불필요한 리-랜더링을 방지하기 위해, 즉, 리액트의 성능을 위해 한꺼번에 state를 업데이트하기 때문이라고 한다.