Mahamudul Hasan Rubel
HomeAboutProjectsSkillsExperienceBlogPhotosContact
Mahamudul Hasan Rubel

Senior Software Engineer crafting high-performance web applications and SaaS platforms.

Navigation

  • Home
  • About
  • Projects
  • Skills
  • Experience
  • Blog
  • Photos
  • Contact

Get in Touch

Available for senior/lead roles and consulting.

bd.mhrubel@gmail.comHire Me

© 2026 Mahamudul Hasan Rubel. All rights reserved.

Built with using Next.js 16 & Tailwind v4

Back to Blog
ReactNext.jsJune 22, 20264 min read

React rendering: Mastering State Batching and the Two-Pass Model

React rendering follows a two-pass mental model involving state batching and reconciliation. Learn how React Fiber manages the render and commit phases.

ReactFrontendJavaScriptWeb PerformanceState ManagementNext.jsTutorial

Last month, I spent about three hours debugging a performance bottleneck in a dashboard component. Every time a user toggled a filter, the entire UI flickered, and the browser locked up for roughly 180ms. It wasn't a complex app, but my misunderstanding of how React processes updates was causing unnecessary work.

If you’re still thinking of React updates as "change state, UI updates immediately," you're going to hit a wall. To write performant code, you have to adopt the "Two-Pass" mental model: the Render phase and the Commit phase.

Understanding the Two-Pass Render Phase

When you call setState in React, nothing happens to the DOM right away. React doesn't just push pixels to the screen; it schedules a task. This is where React rendering becomes interesting.

The process splits into two distinct stages:

  1. The Render Phase: React calls your components, executes your logic, and calculates what needs to change. It’s a pure, synchronous process where React compares the current virtual DOM with the new one.
  2. The Commit Phase: React takes the results of that comparison and applies the changes to the actual browser DOM. This is the only part where DOM nodes are touched.

We first tried to fix my dashboard performance issue by wrapping everything in memo, but that didn't help. We were triggering too many renders because we didn't account for how React groups these updates.

How State Batching Optimizes Updates

React doesn't perform a render pass for every single setState call. If you have a function that updates state three times in a row, React batches those updates into a single render cycle.

JAVASCRIPT
const handleClick = () => {
  setCount(c => c + 1);
  setCount(c => c + 1);
  setCount(c => c + 1);
  // React batches these. 
  // It only re-renders once after this function finishes.
};

This is a massive performance win. In earlier versions of React, batching was limited, but since React 18, it’s automatic even inside promises, timeouts, and native event handlers. Understanding this React rendering behavior is crucial because it prevents your application from doing redundant work.

The Role of React Fiber

Under the hood, this two-pass system is powered by React Fiber. Think of Fiber as a specialized engine that can pause, resume, and abandon work.

Because the Render phase is pure, React can "interrupt" it. If a higher-priority task comes in—like a user typing in an input field—React can drop the current Render phase, handle the input, and then restart the work on your dashboard.

When you dig into the React rendering lifecycle, you'll see why keeping your render logic "pure" is non-negotiable. If you perform side effects (like API calls or random number generation) directly in your component body, React might run that code multiple times without ever committing it to the screen.

Reconciliation: The Decision Maker

Once the Render phase finishes, React has a "diff" of what changed. This is the heart of reconciliation. React uses a tree-diffing algorithm to determine the minimum number of operations required to update the DOM.

If you’re working with lists, you’ve likely seen the warning about key props. React keys and reconciliation are the primary way React tracks identity during this pass. If your keys are unstable (like using Math.random()), React can't track elements correctly, leading to massive re-renders that ruin your performance.

Practical Tips for Juniors

If you're feeling overwhelmed, here’s my advice:

  • Don't fear the render: It’s okay for components to re-render. React is fast. Only optimize when you actually see a performance lag.
  • Keep render logic clean: Never put fetch or complex calculations directly in the component body. Use useEffect or useMemo.
  • Batching is your friend: Rely on the fact that React will group your state updates. Don't try to "force" updates to be synchronous.
  • Check your dependencies: If you're struggling with useState and useEffect, simplify your state. Complex state usually leads to complex, hard-to-debug render cycles.

I’m still not perfect at this. Sometimes I’ll still write a component that re-renders more than it should, and I have to go back in with the React DevTools Profiler to figure out why. The goal isn't to write "zero render" code; it's to write code where you understand why the browser is painting what it’s painting.

Next time you’re debugging, ask yourself: "Is this code in the render phase or the commit phase?" It changes everything.

Frequently Asked Questions

Does React batch state updates in setTimeout? Yes. Since React 18, batching is automatic regardless of where the state update is triggered, including setTimeout, fetch callbacks, and native event listeners.

Can I stop a re-render from happening? You can use React.memo, useMemo, or useCallback to prevent components from re-rendering if their props haven't changed. However, always profile your app first—premature optimization often adds more complexity than it solves.

What happens if I trigger a state update during the render phase? React will throw an error or enter an infinite loop. You should never update state directly inside the function body of a component. Always trigger state updates inside event handlers or useEffect.

Back to Blog

Similar Posts

ReactNext.jsJune 21, 20264 min read

React State Synchronization: How to Avoid Infinite Loops

Master React state synchronization by avoiding unnecessary useEffect calls. Learn to handle dependent inputs correctly and keep your components predictable.

Read more
ReactNext.jsJune 22, 2026
4 min read

React useEffect: A Synchronization Tool, Not State Management

React useEffect is for synchronizing external systems, not state management. Learn why treating it as a lifecycle method leads to bugs and how to fix it.

Read more
ReactNext.jsJune 22, 20264 min read

React keys and reconciliation: Why stable identity matters

React keys are essential for efficient reconciliation. Learn why stable component identity prevents UI bugs and performance bottlenecks when rendering lists.

Read more