Master React reconciliation to control how your UI components stay, update, or get destroyed. Stop losing state during re-renders by mastering component identity.
Last week, a junior on my team spent four hours trying to figure out why a form input kept losing focus and resetting its value every time a parent component updated. They were convinced it was a state management issue, but it was actually a classic misunderstanding of how React handles component identity.
If you don't grasp the "Component Life" mental model, you’re essentially guessing at why your UI behaves the way it does.
At its heart, React reconciliation is the process where React compares the new tree of elements you return from your function with the previous one. It’s not just comparing HTML tags; it’s making a decision about whether a component instance is the "same" one it was a moment ago.
When the tree changes, React follows a simple heuristic:
<div> to <section>), React destroys the old component and everything inside it.This is why, if you move a component from one branch of your JSX tree to another, React will completely destroy the state of that component. It thinks it's a brand-new entity. Understanding this is vital when you're looking into React reconciliation and component state persistence: A mental model to keep your app predictable.
Let’s look at a common mistake. Imagine you have a conditional rendering block:
JSXfunction App({ showDetails }) { return ( <div> {showDetails ? <DetailsView /> : <SummaryView />} </div> ); }
If showDetails flips from true to false, React sees that <DetailsView /> is gone and <SummaryView /> has appeared. It doesn't just swap the content; it unmounts DetailsView, runs its cleanup functions, and mounts SummaryView from scratch. If DetailsView held local useState values, they are gone forever.
I once tried to fix a performance issue by wrapping a component in a ternary operator, thinking it would be cleaner. Instead, I inadvertently caused a full re-mount on every toggle, which triggered an expensive useEffect fetch that took around 280ms to complete. It was a disaster for the user experience.
The most powerful tool you have for controlling the component render cycle is the key prop. You’ve likely used it for lists, but it’s actually a way to tell React: "Hey, this component is the same one even if its position in the tree changes."
When you provide a stable, unique key, React uses it to track the identity of that component instance across renders. If you move a component with a stable key from the top of a list to the bottom, React will move the actual DOM node rather than destroying and recreating it.
However, be careful. Using Math.random() as a key is the fastest way to break your app. Because the key changes on every render, React will always destroy and recreate the component, which defeats the purpose of the state persistence you're trying to achieve. If you're struggling with state resetting, check out React useRef and Component Memory: Why Variables Reset on Re-render for a deeper look at how to protect your data.
The actual DOM diffing algorithm is highly optimized. React uses a fiber-based architecture to perform this work in chunks, which prevents the main thread from locking up during large updates.
When you dive into React performance debugging: How to trace component re-renders, you’ll see that most "performance issues" aren't actually about the speed of the diffing algorithm itself. They are about components doing too much work because they are being re-mounted when they should be updated.
Here’s my advice for when you’re building components:
div for a section dynamically) if you want to preserve state.useMemo or memo. Most of the time, React is fast enough. Focus on the identity of your components first.I still find myself occasionally nesting a component definition inside another component function. This is a massive mistake because it creates a new component type on every single render. React sees a "new" component type, destroys the old one, and mounts the new one. Always define components at the top level of your file.
If I could go back to when I started, I’d spend less time worrying about the virtual DOM and more time tracing the lifecycle of a single component instance. Once you see the component as an object that lives as long as its position and key remain stable, the rest of the React lifecycle starts to make a lot more sense.
Why does my component state reset when I re-render? It’s almost always because the component is being unmounted and re-mounted. Check if you’re defining a component inside another component or if you’re using a key that changes on every render.
Does React always re-render everything? No. React only re-renders components that are part of the subtree where the state change occurred. You can use the React Rendering Lifecycle: Why Components Re-render and How to Optimize to limit these updates.
When should I use a key? Use a key whenever you render a list of items or when you need to force a component to re-mount (by changing its key). Never use an index as a key if the list order can change.
React keys are essential for efficient reconciliation. Learn why stable component identity prevents UI bugs and performance bottlenecks when rendering lists.