
[React 18 이전] 이벤트 핸들러 내부에서 발생하는 state 업데이트 ➡️ 배칭 ✅
function App() { const [count, setCount] = useState(0); const [flag, setFlag] = useState(false); function handleClick() { setCount((c) => c + 1); // 아직 리렌더링 X setFlag((f) => !f); // 아직 리렌더링 X // 함수가 끝나면 리렌더링 (배칭!) } return ( <div> <button onClick={handleClick}>Next</button> <h1 style=>{count}</h1> </div> ); }
[React 18 이전] 이벤트 핸들러 밖에서 발생하는 state 업데이트 ➡️ 배칭 ❌
function App() { const [count, setCount] = useState(0); const [flag, setFlag] = useState(false); function handleClick() { fetchSomething().then(() => { // 배칭 X setCount((c) => c + 1); // 리렌더링 발생 setFlag((f) => !f); // 리렌더링 발생 }); } return ( <div> <button onClick={handleClick}>Next</button> <h1 style=>{count}</h1> </div> ); }
- 이벤트가 진행되는 중이 아닌, 완료된 후의 콜백에서 state 업데이트가 발생하기 때문에 배칭되지 않는다.
createRoot를 통해, 어떤 이벤트에서 왔는지와 상관없이 모든 업데이트들이 자동 배칭된다. createRoot가 아닌 레거시 render를 사용할 경우 자동 배칭이 적용되지 않는다.)[React 18 이후] 이벤트 핸들러 밖에서 발생하는 state 업데이트 ➡️ 배칭 ✅
function App() { const [count, setCount] = useState(0); const [flag, setFlag] = useState(false); function handleClick() { fetchSomething().then(() => { setCount((c) => c + 1); setFlag((f) => !f); // 이벤트 핸들링이 끝나고 콜백이 끝나면 리렌더링 (배칭!) }); } return ( <div> <button onClick={handleClick}>Next</button> <h1 style=>{count}</h1> </div> ); }
- 이벤트 진행 중이 아닌, 이벤트 핸들링이 끝난 후 콜백에서 state 업데이트가 일어나고 있지만 배칭이 적용된다.
ReactDOM.flushSync()를 사용하면 배칭을 하지 않고 state 변경 즉시 렌더(DOM 업데이트)가 이루어진다. import { flushSync } from "react-dom"; // Note: react가 아닌 react-dom이다 function handleClick() { flushSync(() => { setCounter((c) => c + 1); }); // DOM 업데이트 flushSync(() => { setFlag((f) => !f); }); // DOM 업데이트 }
레퍼런스