When a parent React component re-renders, a nested child component loses its state if the child’s type or position in the tree changes, or if the child is defined inside the parent’s function body. The most common cause is defining a component inside another component, which creates a new component type on every render and forces React to unmount and remount the child, resetting its state.
How React Re-rendering Works
React re-renders a component when its props or state change, or when its parent re-renders. By default, when a parent re-renders, React re-renders all of its children, even if the children’s props haven’t changed. This is a deliberate design choice to keep the model simple: React doesn’t deeply compare props unless you explicitly opt into memoization.
During a re-render, React runs the component function again, producing a new element tree. It then compares that tree with the previous one (a process called reconciliation) and updates only the parts of the DOM that differ. However, the comparison is not a deep equality check on the elements themselves. Instead, React uses the element’s type and key to decide whether a component instance should be preserved or recreated.
The Core Reason: Component Type Identity
React preserves a component’s state as long as the component type at a given position in the tree is the same between renders. If the type changes, React unmounts the old instance and mounts a new one, wiping all local state (useState, useRef, useReducer, etc.).
The classic mistake is defining a component inside the parent’s render function:
function Parent() {
const [count, setCount] = useState(0);
// WRONG: Child is re-created on every render
function Child() {
const [local, setLocal] = useState(0);
return <div>{local}</div>;
}
return (
<div>
<button onClick={() => setCount(count + 1)}>{count}</button>
<Child />
</div>
);
}
Each time Parent re-renders (e.g., when count changes), a new Child function is created. React sees a different function reference, so it treats it as a different component type and remounts it. The local state resets to 0.
Why Keys Also Matter
Even when the component type stays the same, React uses key to identify which instance corresponds to which element in a list or when elements are reordered. If you render a component in a list without a stable key, or with an index as a key, React may reuse the wrong instance or recreate it when the list changes.
For example, if you have a list of items and you prepend a new item, using the index as a key will cause React to shift the state of all subsequent items. The first item gets the new item’s key, so it unmounts and remounts with fresh state. Using a unique, stable ID as the key prevents this.
How to Fix It: Move the Component Out
The fix is straightforward: define the child component at the top level of the module, outside the parent function. This ensures the component type is a stable reference across renders.
function Child() {
const [local, setLocal] = useState(0);
return <div>{local}</div>;
}
function Parent() {
const [count, setCount] = useState(0);
return (
<div>
<button onClick={() => setCount(count + 1)}>{count}</button>
<Child />
</div>
);
}
Now Child is the same function reference every render, so React preserves its state. If you need to pass data or callbacks, pass them as props. If you need to avoid re-rendering the child when the parent re-renders, wrap the child in React.memo to skip re-renders when props are unchanged.
Other Common Causes of State Loss
- Changing the component type conditionally: Rendering
<A />in one branch and<B />in another will reset state because the type changes. - Using an unstable key: Keys like
Math.random()or the array index (when the list order changes) cause remounts. - Forcing a remount: Setting a
keyon a component that changes (e.g.,<Child key={someId} />wheresomeIdchanges) will intentionally reset state. - Conditional rendering with different positions: If you move a component to a different position in the tree (e.g., from inside a
<div>to outside it), React may treat it as a new instance.
How to Diagnose State Loss
If you’re seeing state reset unexpectedly, check the following:
- Open React DevTools and look at the component tree. If the component shows as a different type (e.g.,
ChildvsChildwith a different reference), that’s the issue. - Add a
console.loginside the child’s render to see if it’s being mounted fresh (you’ll see the log on every parent re-render). - Check if the child is defined inside the parent. Move it out.
- Check if you’re using a
keythat changes. Use a stable ID. - If you’re using
React.memo, ensure the comparison function is correct—a faulty custom comparator can cause remounts or stale props.
Frequently Asked Questions
Does React re-render all children when a parent re-renders?
Yes, by default. When a parent re-renders, React re-renders all of its children, regardless of whether their props changed. This is because React doesn’t perform prop comparison by default. You can opt out by wrapping a child in React.memo or using useMemo/useCallback to stabilize props.
Why does my component lose state when I add a new item to a list?
This usually happens when you use the array index as the key. When you insert an item at the beginning, all subsequent items shift indices, so React sees a different key for each and remounts them. Use a unique, stable identifier (like an ID) as the key to preserve state.
Can I use useRef to preserve state across re-renders?
useRef preserves its value across re-renders, but it is still reset when the component unmounts. If the component is remounted due to a type change or key change, the ref is reinitialized. To truly preserve state across remounts, you’d need to lift the state up to a parent that doesn’t remount, or use a state management library like Redux or Context.
Is it bad to define components inside other components?
Yes, it’s a common anti-pattern because it causes the child to remount on every parent render, leading to state loss and performance issues. Always define components at the top level of a module. If you need to pass dynamic values, use props.
Does React.memo prevent state loss?
No, React.memo only prevents re-renders when props are shallowly equal. It does not prevent remounting. If the component type changes or the key changes, it will still unmount and remount, losing state. React.memo is about performance, not state preservation.
What is the difference between unmounting and re-rendering?
Re-rendering means the component function runs again, but the instance (and its state) is preserved. Unmounting means the instance is destroyed, and all local state is lost. A remount is an unmount followed by a mount, so state resets. React decides between these based on the element type and key.
Related
Formula used
in t | Finish time | min | 50 | 0 in d | Distance | km | 10 | 0 | 100000 out pace | Average pace | min/km | t / d | 2 out speed | Average speed | km/h | d / (t / 60) | 2
Each line is one input (in, sel, list) or one result (out: label, unit, formula, decimals). How the calculators work
Method: the standard formula for this calculation, rounded as each result states. See how the calculators work or report an error.
More cards in this drawer
- Motorola Phone Recovery: How to Recover Data
- Poco X3 GT Dimensity 1100 67W Fast Charging Launched?5.00 min/km
- Flash Converter: What It Is & How to Use It
- Choosing a Pipe Flow Meter for Swirling Flow
- Convert 2D Videos to Oculus Rift VR: How-To
- DRAM Light on Motherboard: Meaning & Fixes5.00 min/km