Artem Krasovskyi’s Post

I used to think React was just "smart about re-renders." Then I actually dug into how reconciliation works. And I realized I had been writing components that quietly fought the algorithm for years. Here's what's actually happening under the hood — and why it matters for your app's performance. — When state changes, React doesn't touch the real DOM immediately. It builds a new Virtual DOM tree, compares it to the previous one, and figures out the minimum set of changes needed. That comparison process is reconciliation. The naive version of this problem is O(n³). For 1000 elements — that's a billion comparisons. Completely unusable. So React cheats. In a smart way. It uses a heuristic O(n) algorithm built on two assumptions: → Elements of different types always produce different trees → Keys tell React which elements are stable across renders Simple rules. Massive performance gain. — Here's where it gets interesting — and where most bugs actually come from. Rule 1 in practice: If you swap a <div> for a <section>, React tears down the entire subtree and rebuilds from scratch. Every child component unmounts. All local state is lost. This isn't a bug. It's the algorithm doing exactly what you told it to do. I've seen this cause subtle bugs in forms — a wrapper element change during a conditional render, and suddenly input state resets mid-user interaction. — Rule 2 in practice — the key trap: Using array index as a key is one of the most common mistakes I've reviewed in code. If you have a list and insert an item in the middle, every index below it shifts. React sees completely new keys, throws away the existing nodes, and rebuilds them. What looked like a simple insert becomes a full list re-render. Use stable, unique IDs. Always. — Fiber changed everything in React 16. Before Fiber, reconciliation was synchronous — once started, it couldn't be interrupted. A heavy render could block the main thread and freeze the UI. Fiber broke rendering into small units of work. React can now pause, prioritize, and resume rendering. That's what powers Concurrent Mode, Suspense, and transitions. The algorithm didn't change. The scheduler around it did. — Practical things I now do differently because of this: → Never create component definitions inside render — new reference = React thinks it's a different type = full unmount every render → Keys on lists always come from data, never from index → Wrap stable subtrees in React.memo when the parent re-renders frequently → Use the Profiler in DevTools to actually see which reconciliation decisions are expensive — Reconciliation is one of those things that's easy to ignore until performance starts hurting. But once you understand the two rules React operates on, a lot of "React behaves weirdly" moments suddenly make complete sense. What's the most unexpected reconciliation bug you've run into? #react #frontend #javascript #webdev #reactjs #frontenddevelopment #softwaredevelopment

  • graphical user interface, application

For Rule 2 - I would add that it’s okay to use indexes as long as the array is static, using unique identifiers as keys is usually done for arrays that come from API endpoints

To view or add a comment, sign in

Explore content categories