React already batches updates inside event handlers, but outside of those — like in setTimeout
or Promise
callbacks — you might hit multiple renders. Here’s how to create a custom hook that forces batching everywhere, minimizing re-renders and saving performance without needing external libraries like Recoil or Jotai.
Why Manual Batching Matters
Use cases where batching helps:
- Complex forms with many fields updated in sequence
- Animations or state transitions tied to promises or timers
- Micro-optimizing deeply nested component trees
Step 1: Create a useBatchState Hook
// useBatchState.js import { useState, useRef } from "react"; import { unstable_batchedUpdates } from "react-dom"; // Works in React 18+ export function useBatchState(initialState) { const [state, setState] = useState(initialState); const pending = useRef([]); function batchUpdate(updater) { pending.current.push(updater); } function flush() { if (pending.current.length === 0) return; unstable_batchedUpdates(() => { setState(prev => { return pending.current.reduce((acc, fn) => fn(acc), prev); }); }); pending.current = []; } return [state, batchUpdate, flush]; }
Step 2: Using the Hook
// ExampleComponent.js import { useBatchState } from "./useBatchState"; function ExampleComponent() { const [count, batchUpdate, flush] = useBatchState(0); function complexIncrement() { batchUpdate(prev => prev + 1); batchUpdate(prev => prev + 1); batchUpdate(prev => prev + 1); flush(); // Forces all updates to apply at once } return ( <div> <p>Count: {count}</p> <button onClick={complexIncrement}>Increment by 3</button> </div> ); } export default ExampleComponent;
Step 3: Notes on React 18+
React 18 batches most things by default, but custom hooks like this still give you absolute control for rare cases or legacy codebases that need it.
Pros and Cons
✅ Pros
- Fewer renders = faster UI for complex state interactions
- No external dependencies
- Works across async event boundaries
⚠️ Cons
- Manual flushing required — if you forget it, you'll have stale updates
- Can overcomplicate simple cases — don't overuse unless profiling says it's needed
🚀 Alternatives
- React 18 automatic batching: Works out of the box for most cases, but not 100% for all async flows.
- State libraries like Jotai, Recoil: Handle fine-grained state natively with batched atoms/selectors.
Summary
Smart batching gives you superpowers when optimizing performance-critical components. Use it when profiling shows wasteful renders — not just because you can.
For a much more extensive guide on getting the most out of React portals, check out my full 24-page PDF file on Gumroad. It's available for just $10:
Using React Portals Like a Pro.
If you found this useful, you can support me here: buymeacoffee.com/hexshift
Top comments (0)
Some comments may only be visible to logged-in visitors. Sign in to view all comments.