[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 업데이트 }
레퍼런스