Batch "일괄"
Batching "일괄처리"
"Batching is when React groups multiple state updates into a single re-render for better performance."
배칭은 더 나은 성능을 위해서 리액트가 여러개의 상태를 한번의 리렌더링에 업데이트 하는 것을 이야기 한다.
- Dan Abramov React Developer
- 단기간에 일어나는 상태변화를 매번 렌더링하지 않고, 일괄 처리한다.
- 리액트 이벤트 단위로 배칭이 발생한다.
- 배칭은 리액트의 상태 값을 일정한 주기로 처리하는 작업. 이벤트로 인해 변경되는 점이 하나든 여러개든 일정한 주기에 맞춰서 다 같이 처리될 수 있도록하는 리액트의 내부기능.
상태값 변경 > 변경된 상태값을 중심으로 가상돔 생성 > 이 때 만든 가상돔을 현재의 최신 UI의 가상돔과 비교
> 적용 > 새로운 돔에 맞춰서 ui 변경
- 상태값 변화를 렌더링하고(리액트에 알려주고) 조정하는 일은 리액트에서 사용하고 있는 Fiber의 도움을 받는다.
> 리액트 17까지는 프로미스가 사용된 코드를 배칭의 범위에 포함하지 못했다.
const handleClick = async() => {
await sleep(0); // promise
const randomA = getRandomNumber();
setA(randomA);
setB(MAX - randomA);
}
리액트 17에서는 이 경우에 setA와 setB는 각각 따로 실행되게 된다. 배칭이 되지 않는다.
이 경우에 setState가 여러개가 들어가게 되면 성능이 크게 저하될 수 있다.
> 리액트 18에서는?
- createRoot 함수를 사용해서 리액트 루트 컴포넌트를 초기화하면, 프라미스와 타이머도 같은 주기 내에서 배칭할 수 있다. 이를 [자동배칭]이라고 한다.
근데 useEffect 의 경우에 안에서 setState를 사용하는 경우는 대부분 UI를 즉각적으로 업데이트하고 싶을 경우가 많다.
하지만 대부분의 경우, 의도대로 동작하지 않는다.
const [value, setValue] = useState(0);
useEffect(()=>{
setValue(1);
// value를 쓰면 1이 나올 거라고 생각하지만 0이다.
console.log(value);
setValue(2);
// 배칭 때문이다.
},[])
> 그렇다면 배칭을 하고 싶지 않다면?
ReactDOM.flushSync()를 사용하면 배칭하지 않을 수 있다.
import { flushSync } from "react-dom"; // Note: react가 아닌 react-dom이다
function handleClick() {
flushSync(() => {
setCounter((c) => c + 1);
});
// 이 과정이 끝났을 때 React는 DOM을 업데이트한 상태이다
flushSync(() => {
setFlag((f) => !f);
});
// 이 과정이 끝났을 때 React는 DOM을 업데이트한 상태이다
}