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.
If you found this useful, you can support me here: buymeacoffee.com/hexshift
Top comments (1)
Can you accept my invitation so that I can get a free gift?
temu.com/u/XtD2852IPG47cIB
The person who will install and login with this Referral link can buy anything here free