惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

宝玉的分享
宝玉的分享
酷 壳 – CoolShell
酷 壳 – CoolShell
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
S
Security @ Cisco Blogs
小众软件
小众软件
D
Docker
博客园_首页
A
About on SuperTechFans
P
Privacy International News Feed
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
A
Arctic Wolf
Spread Privacy
Spread Privacy
有赞技术团队
有赞技术团队
T
Tailwind CSS Blog
Latest news
Latest news
WordPress大学
WordPress大学
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
T
The Exploit Database - CXSecurity.com
C
Cybersecurity and Infrastructure Security Agency CISA
大猫的无限游戏
大猫的无限游戏
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
K
Kaspersky official blog
V2EX - 技术
V2EX - 技术
SecWiki News
SecWiki News
U
Unit 42
GbyAI
GbyAI
H
Hackread – Cybersecurity News, Data Breaches, AI and More
L
LINUX DO - 热门话题
S
Security Affairs
Y
Y Combinator Blog
aimingoo的专栏
aimingoo的专栏
Blog — PlanetScale
Blog — PlanetScale
MongoDB | Blog
MongoDB | Blog
博客园 - 【当耐特】
The GitHub Blog
The GitHub Blog
T
Tenable Blog
W
WeLiveSecurity
Cloudbric
Cloudbric
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
G
Google Developers Blog
Application and Cybersecurity Blog
Application and Cybersecurity Blog
N
Netflix TechBlog - Medium
F
Full Disclosure
N
News and Events Feed by Topic
D
DataBreaches.Net
P
Proofpoint News Feed
B
Blog RSS Feed
B
Blog
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant Common SOC 2 Failures (Real World) Stop Vibe-Checking Your AI App: A Practical Guide to Evals How to Use SonarQube and SonarScanner Locally to Level Up Your Code Quality Your Next To-Do App Is Dead — I Replaced Mine with an OpenClaw AI Sign a Nostr event in 60 lines of Python using coincurve — no nostr-sdk, no nbxplorer, no rust toolchain ITGC Audit Explained Like You’re in Big 4 Patch Tuesday abril 2026: Microsoft parcha 163 vulnerabilidades y un zero-day en SharePoint Stop scraping everything: a better way to track competitor price changes Listing on MCPize + the Official MCP Registry while routing payments OUTSIDE the marketplace — how I kept 100% of my x402 revenue Building an AI-Powered Risk Intelligence System Using Serverless Architecture Why We Ripped Function Overloading Out of Our AI Toolchain Testing AI-Generated Code: How to Actually Know If It Works SaaS Churn Is Killing Your Business. Here Is What to Do About It (Without a Support Team) The Speed of AI Is No Longer Linear - And Self-Improving Models Are Why How to Implement RBAC for MCP Tools: A Practical Guide for Engineering Teams From Standard Quote to Persuasive Proposal: AI Automation for Arborists I built a CLI that scaffolds complete multi-tenant SaaS apps Axios CVE-2025–62718: The Silent SSRF Bug That Could Be Hiding in Your Node.js App Right Now The dashboard that ended our friendship Data Pipelines Explained Simply (and How to Build Them with Python) The Hidden Cost of AI Systems Nobody Talks About. undefined vs undeclared, and how typeof behaves Switching from file-based jobs to NATS/Kafka in Rust without changing code io_uring Adventures: Rust Servers That Love Syscalls Why Agentic AI is Killing the Traditional Database The POUR principles of web accessibility for developers and designers Quantum Neural Network 3D — A Deep Dive into Interactive WebGL Visualization How To Install Caveman In Codex On macOS And Windows Automation Pipeline Reliability: Why Your Workflow Breaks When Nobody Is Watching I Built an 'Open World' AI Coding Agent — It Works From ANY Folder From Freelancing to Product: A Tech Service Company's SaaS Transformation China's AI Giants: Adding Tencent Hunyuan & ByteDance Doubao to AI University (74 Providers) On the Vibe Coders and Their Lies clerk: Auto-Summarize Your Claude Code Sessions AI Weekly — 2026/04/10–04/17 | The Model Lockdown Is Here, but the Toolchain Is the Real Battleground AI 週報 — 2026/04/10–2026/04/17 模型封鎖潮來了,但工具鏈才是真戰場 Maybe this is how Open-Source apps are born... 🚀 Fine-Tune LLMs with LoRA and QLoRA: 2026 Guide tRPC v11 + Next.js App Router: End-to-End Type Safety Without the Boilerplate ShadCN UI in 2026: Why I Stopped Installing Component Libraries and Started Owning My Components SaaS Billing in React Server Components: Stripe + Supabase Without a Single `useEffect` Join our DEV Weekend Challenge — $1,000 in Prizes Across TEN winners! Submissions Due April 20 at 6:59 AM UTC. Implementing FSRS Spaced Repetition in Flutter + Supabase — Adding Memory Science to an AI Learning App "I Texted My Localhost From the Train — Claude Code Fixed the Bug Before I Got Home" I Built a Sales Prep AI and It Went Deeper Than Expected Design to Code #2: One JSON, Eleven Outputs Solving the 100M-Row Problem: A Summary Table Pattern for High-Volume Push Notification Logs Flutter Web With Wasm: What Actually Changes For Developers I Built 50 Royalty-Free Soundtracks for My Side Project in a Weekend Using AI Music Generation The Vibe Coding Security Checklist: 7 Things to Check Before You Ship Stop Letting Googlebot Guess Fix Your React App's SEO Right Desconstruindo o Streaming do LinkedIn: Como Criar um Engine de Extração de Vídeo de Alta Performance com HLS e FFmpeg (EDA Part-1) EDA (Exploratory Data Analysis) Explained With Real Life — Why Looking at Your Data Is the Most Important Step in Machine Learning Brand Relationship Management at Scale: Our 4-Touch Outreach System for 200+ Brands Why String.fromEnvironment() Might Return an Empty String in Dart JGuardrails 1.0.0 — Hardening Java LLM Apps Against Jailbreaks, Toxicity, and Prompt Injection Plan and Schedule a Full Week of Threads Content From One Claude Conversation Coding Cat Oran Ep3, Five Tables Changed Everything Updated: BFF Pattern I'm done watching freelancers get buried by 200 proposals. So I'm building the alternative. This is my first post BFS Algorithm in Java Step by Step Tutorial with Examples Tracking LLM Pricing Monthly: An Open Dataset for 22 AI Models How We Measure Content ROI on a Comparison Site: Revenue Attribution Without Perfect Data Introducing Nova AI Ops: The AI-Native Operating System for SRE Teams I built a free desktop video downloader for Windows — Grabbit How Talkie OCR Helps Vision-Impaired & Dyslexic Users Read the World Around Them VRCFaceTracking安装和iPhone面捕配置教程,有bug Even CrowdStrike Can't See Your Agents The Automation Gold Rush: What n8n Workflows and Claude Are Opening Up for Developers Right Now
React Re-rendering: When and Why Component Trees Update
Ashish Kumar · 2026-04-26 · via DEV Community

Related: Long Tasks and Main Thread Blocking heavy React renders are one of the most common sources of Long Tasks.

React's default re-render behavior is intentionally conservative: when a parent re-renders, all children re-render too. This is correct by default — React prioritizes correctness over performance, and render-phase work (function calls, hook execution) is cheap enough that unnecessary re-renders are often harmless. But "often harmless" is not "always harmless."

What this covers: The exact four triggers that cause a component to re-render, how reconciliation and the fiber tree work, why referential equality matters for memoization, the context performance trap, and how to read the React DevTools Profiler to find the root cause of unexpected re-renders.

Diagram of React's render phase versus commit phase: reconciliation produces an effect list, then the DOM is updated.


Render phase vs commit phase

React's work of "updating the UI" is split into two fundamentally different phases. Confusing them is the root of a lot of performance misconceptions.

The render phase

The render phase is when React calls your component functions and figures out what the UI should look like. When you call setState, React schedules a render. During the render phase, React:

  1. Calls the component function (your function component body executes).
  2. Calls all the hooks in order (useState, useEffect, useMemo, etc.).
  3. Gets the returned JSX.
  4. Does this recursively for any child components that also need updating.
  5. Diffs the new output against the previous output (reconciliation).

Critical insight: the render phase is pure work: React is just computing what the UI should be. **It doesn't touch the DOM yet.* Your component function can be called and produce output that React then decides to discard (this is what StrictMode's double-render exploits see below).

The commit phase

The commit phase is when React actually applies changes to the DOM. It has three sub-phases:

  1. Before mutation: fires getSnapshotBeforeUpdate lifecycle and captures current DOM state.
  2. Mutation: applies DOM insertions, updates, and deletions. This is the only time React directly touches the DOM.
  3. Layout: fires useLayoutEffect and componentDidMount/componentDidUpdate synchronously. This is why useLayoutEffect can measure DOM layout the DOM is updated but the browser hasn't painted yet.
  4. After the commit, useEffect callbacks are scheduled for after the browser has painted.

Understanding this split matters because: render phase work is cheap to do multiple times (it's just function calls and object comparisons). Commit phase work touches the DOM and can trigger browser reflow. React is clever about doing as little DOM work as possible.


Reconciliation and the fiber tree

React doesn't just compare new JSX against a flat list of DOM elements. It maintains an internal data structure called the fiber tree a graph of objects representing every component instance in your app, including their state, hooks, and pending work.

Each fiber corresponds to one component instance. When a state update is triggered, React creates an alternative "work in progress" fiber tree and starts reconciling it against the current tree. This is the reconciliation algorithm.

What reconciliation does:

  • Compares new JSX element type against the existing fiber's type.
  • If the type changed (e.g., <Button> became <a>), React unmounts the old component tree and mounts a fresh one.
  • If the type is the same, React updates the existing fiber with new props, runs hooks again, and recurses into children.
  • For lists, it uses keys to match new elements to existing fibers.

The key performance insight: reconciliation is proportional to the size of the fiber tree that gets re-rendered. If you trigger a re-render high in the tree, React walks down through all descendants. This is why the settings checkbox caused 47 re-renders the useState was placed in a component that was the parent of almost everything.


What triggers a re-render

There are exactly four causes of a React component re-rendering:

Trigger Description
setState call Calling the setter from useState or useReducer dispatch
Props change Parent re-renders and passes new prop values (by reference)
Context value changes Any consumer of a context re-renders when the context value changes
Parent re-renders A component re-renders when its parent re-renders, even if props didn't change

The fourth one surprises people the most. In React's default behavior, if a parent re-renders, all children re-render too regardless of whether their props changed. This is the default: without memoization, the component tree re-renders in a cascade from the component that triggered the state change, downward.

This is actually a deliberate design choice. React assumes that re-rendering is cheap (it's just function calls) and that computing whether to skip a render is sometimes more expensive than just doing the render. The defaults are optimized for correctness, not maximum performance.


Re-renders don't mean DOM updates

This is a crucial clarification that trips up a lot of performance investigations.

When 47 components re-render, React calls 47 component functions and gets back 47 JSX trees. Then it diffs those against the previous output. If the output is identical, React makes zero DOM changes for that component. No DOM mutations, no browser reflow, nothing.

So "47 components re-rendered" in React DevTools means "47 component functions were called, not "47 DOM nodes were updated." The actual DOM impact depends on how many of those components produced different output.

This distinction matters because: pure render cost (function calls, hook execution) is usually cheap. DOM mutation cost is what triggers browser layout and paint. If your 47 re-rendering components all produce the same output as before, you might have wasted 2ms of JavaScript time, but you've caused zero additional browser rendering work.

That said, 47 function calls isn't free: if any of those functions do expensive computations inline (without useMemo), or if React itself has to run through complex reconciliation logic, you'll feel it.


Why referential equality matters

This is where function components differ fundamentally from the mental model of "props changed = new values." React uses referential equality (===) to compare props and determine if a memoized component should skip its render.

function Parent() {
  const [count, setCount] = useState(0);

  // ⚠️ New object reference created on every render
  const config = { theme: 'dark', size: 'medium' };

  // ⚠️ New function reference created on every render
  const handleClick = () => setCount(c => c + 1);

  return (
    <>


    </>
  );
}

Enter fullscreen mode Exit fullscreen mode

Every time Parent re-renders, config and handleClick are brand new objects. They're deeply equal to the previous values (same shape, same content) but config === previousConfig is false because they're different object references.

If Settings is wrapped in React.memo, it will still re-render because its config prop is a new reference, even though nothing meaningfully changed.

// useMemo stabilizes the reference between renders
const config = useMemo(() => ({ theme: 'dark', size: 'medium' }), []);

// useCallback stabilizes function references
const handleClick = useCallback(() => setCount(c => c + 1), []);

Enter fullscreen mode Exit fullscreen mode

useMemo and useCallback are not about avoiding "expensive computations: they're primarily about referential stability. Their main job is to prevent downstream re-renders caused by new object/function references.


The context performance problem

Context is often described as a solution for "prop drilling: passing data through many levels of components. It is that. It's also a performance footgun if you're not careful about what you put in it.

Context is a broadcast. When the context value changes, every component that consumes that context re-renders, regardless of whether the specific piece of data it reads changed.

// ⚠️ This context re-renders ALL consumers when ANYTHING in the object changes
const AppContext = createContext({
  user: null,
  theme: 'light',
  notifications: [],
  sidebarOpen: false,
});

function App() {
  const [sidebarOpen, setSidebarOpen] = useState(false);

  // New object reference when sidebarOpen changes
  // → Every context consumer re-renders, including deep UI components that only care about `user`
  const value = { user, theme, notifications, sidebarOpen };

  return <AppContext.Provider value={value}>{children}</AppContext.Provider>;
}

Enter fullscreen mode Exit fullscreen mode

Toggling the sidebar causes every consumer of AppContext to re-render, including components that only ever read user. The fix is to split context by update frequency:

// Stable values that rarely change
const UserContext = createContext(null);
// Dynamic values that change often
const UIStateContext = createContext({ sidebarOpen: false });

// Now sidebar state changes only affect UIStateContext consumers

Enter fullscreen mode Exit fullscreen mode

A useful mental model: each context should have one reason to change. If your context value object contains both stable auth data and frequently-changing UI state, you'll cause unnecessary re-renders across the entire consumer tree.


Keys in lists: what they actually do

Keys serve a specific mechanical purpose during reconciliation. React uses keys to match new list elements to existing fibers when the list changes. Without keys (or with incorrect keys), React falls back to matching by position.

// Without keys  React matches by index
// Adding an item to the beginning: React thinks EVERY item changed
{items.map((item, index) => (

))}

// With stable IDs: React correctly identifies which item was added
{items.map(item => (

))}

Enter fullscreen mode Exit fullscreen mode

When you use key={index} and prepend an item to the list, React sees:

  • Position 0: had {id: 1}, now has {id: 0} → update this fiber
  • Position 1: had {id: 2}, now has {id: 1} → update this fiber
  • etc.

Every fiber gets updated because positions changed, even though only one item was added. With stable IDs, React sees that existing fibers just shifted position and correctly reconciles with minimal work.

The random-key anti-pattern is even worse:

// ⚠️ Generates a new key on every render  destroys all reconciliation benefits
{items.map(item => (

))}

Enter fullscreen mode Exit fullscreen mode

With random keys, every render unmounts all existing Card components and mounts fresh ones. You lose all component state, all DOM node reuse, and get maximum mount/unmount work on every render.


React 18 automatic batching

Before React 18, state updates inside setTimeout, Promise.then, or native event handlers were processed individually: one setState = one re-render.

// React 17: 2 renders
setTimeout(() => {
  setCount(c => c + 1); // Render 1
  setLoading(false);    // Render 2
}, 1000);

// React 18: 1 render (automatic batching)
setTimeout(() => {
  setCount(c => c + 1); // Batched
  setLoading(false);    // Batched → single render
}, 1000);

Enter fullscreen mode Exit fullscreen mode

React 18's automatic batching extends the existing batching behavior (which previously only worked in React event handlers) to all asynchronous contexts. This is a free performance improvement that many apps benefit from immediately after upgrading.

The cases where this matters most: fetch callbacks that update multiple state values, async event handlers that set loading + data states together, and any code that does multiple setState calls in a row in async code.

If you need to explicitly opt out of batching (rare), flushSync from react-dom forces synchronous processing:


// Forces immediate render after each setState
flushSync(() => setCount(1));
flushSync(() => setLoading(false));

Enter fullscreen mode Exit fullscreen mode


StrictMode's double-render in development

If you're using React.StrictMode (you should be in development), your component functions are called twice during the render phase in development mode. This is intentional and a source of confusion for developers who see their console.log appearing twice.

What StrictMode is doing: it deliberately calls your component function twice to check that the function is pure that calling it multiple times with the same inputs produces the same output. If your component has side effects in the render phase (network requests, direct DOM mutations, setting external variables), the double-render will expose them because those effects will fire twice.

function BadComponent() {
  // ⚠️ Side effect in render  this fires twice in StrictMode development
  someGlobalCounter++;

  return <div>{someGlobalCounter}</div>;
}

Enter fullscreen mode Exit fullscreen mode

The double-render only happens in development mode. Production builds render once. If something works in development but breaks in production differently, StrictMode's double-render is not the cause but the bugs it exposes in development might surface as subtle issues in production.


Reading the React Profiler

The React DevTools Profiler is the right tool for understanding re-renders. Open DevTools → Components → Profiler. Hit Record, do the interaction that feels slow, stop recording.

The flame chart shows every component that rendered, how long it took, and crucially why it rendered:

  • Props changed one or more props have a different reference than the previous render.
  • State changed a useState or useReducer hook in this component changed.
  • Context changed a context this component subscribes to changed.
  • Parent re-rendered no local reason, but the parent rendered so this did too.
  • Hooks changed a hook produced a different value than before.

The "why did this render?" information is invaluable for diagnosing unnecessary re-renders. When you see "parent re-rendered" for a component that shouldn't care about its parent's state change, that's the target for React.memo. When you see "props changed" for a memoized component that's receiving an object prop, that's the target for useMemo.

Profiler "Why" Likely fix
"Parent rendered" on a pure display component Wrap with React.memo
"Props changed" on a memoized component Stabilize prop reference with useMemo/useCallback
"Context changed" but component only reads one field Split context by update frequency
"State changed" unexpected Check if the state is co-located at the right level

The Profiler also shows total render duration per component. If a single component consistently takes 15ms+ to render, that's a component-level optimization opportunity either memoizing expensive calculations inside it or splitting it into smaller pieces.


Read the original article on Renderlog.in:
https://renderlog.in/blog/react-rerendering-when-trees-update/

If you found this helpful, I've also built some free tools for developers and everyday users. Feel free to try them once:

JSON Tools: https://json.renderlog.in
Text Tools: https://text.renderlog.in
QR Tools: https://qr.renderlog.in