Frontend/React
[React] useState의 비동기 처리와 함수형 업데이트
오이가지아빠
2021. 7. 11. 21:17
#1. setState는 비동기로 동작한다
아래와 같이 동작하는 코드를 보자.
하나는 숫자를 1씩 증가시키는 버튼이고, 하나는 숫자를 1씩 감소시키는 버튼이다.
import React, { useState } from "react"
function App() {
const [num, setNum] = useState(1)
async function plus() {
setNum(num + 1)
}
async function minus() {
setNum(num - 1)
}
return (
<div className="App">
<h1>{num}</h1>
<button onClick={plus}>PLUS</button>
<button onClick={minus}>MINUS</button>
</div>
);
}
export default App;
아직까지는 잘 작동하고 있지만, 요건이 바뀌어서 1씩 증가하던것을 3씩 증가시키기 위해 아래처럼 변경했다고 치자.
import React, { useState } from "react"
function App() {
const [num, setNum] = useState(1)
async function plus() {
setNum(num + 1)
setNum(num + 1)
setNum(num + 1)
}
async function minus() {
setNum(num - 1)
}
return (
<div className="App">
<h1>{num}</h1>
<button onClick={plus}>PLUS</button>
<button onClick={minus}>MINUS</button>
</div>
);
}
export default App;
setNum(num + 1) 을 3번 호출했기 때문에 버튼 한번으로 3씩 증가할것으로 예상되지만, 여전히 1씩 증가하게 된다.
왜 그럴까?
동일한 state를 연속적으로 업데이트 하는 경우, 리액트는 setState를 각각 동기로 수행하지 않고, batch 처리한다.
전달된 setState를 하나로 병합한 후 최종적으로 한번의 setState를 하게 된다는 얘기다.
위의 결과처럼 병합을 수행할 때, 동일한 key에 대해 이전의 값을 계속 덮어 쓰기 때문에, 결국 마지막 명령어만 수행되는 셈이다.
#2. 함수형 업데이트
결국 문제의 해결은 setState를 비동기로 수행할 때, 값을 전달하지말고 업데이트된 최신의 state와 함께 함수를 전달하는 방법이다.
react의 setState코드를 보면 다음과 같이 값을 받거나, 이전 state와 함께 함수를 전달받을 수 있도록 되어있다.
// React Hooks
// ----------------------------------------------------------------------
// based on the code in https://github.com/facebook/react/pull/13968
// Unlike the class component setState, the updates are not allowed to be partial
type SetStateAction<S> = S | ((prevState: S) => S);
이렇게 되면, 여러번 전달받는 함수들은 큐에 저장되어 순서대로 실행된다. 따라서 큐에서 먼저 수행된 함수의 결과로 반영된 state값이 다음 수행할 함수의 인자로 들어가게 되므로, 항상 최신의 state를 유지하게 된다.
import React, { useState } from "react"
function App() {
const [num, setNum] = useState(1)
async function plus() {
setNum(num => num + 1)
setNum(num => num + 1)
setNum(num => num + 1)
}
async function minus() {
setNum(num - 1)
}
return (
<div className="App">
<h1>{num}</h1>
<button onClick={plus}>PLUS</button>
<button onClick={minus}>MINUS</button>
</div>
);
}
export default App;
이렇게 하게되면 아래와 같이 정상적으로 동작하는 것을 확인할 수 있다.
반응형