Why Do Some React Child Components Re‑Render While Others Don’t?
This article explains how parent and child components interact in React, why certain child components re‑render on state changes, and how techniques like React.memo, useCallback, and useMemo can be used to control rendering performance.
This article explores the relationship between React elements, child components, and parent components, and how they affect re‑rendering.
Child components, parent components and their impact on re‑rendering
In React, components are the basic building blocks of the UI. They can be divided into parent and child components, and understanding how their relationship influences re‑rendering is crucial.
"Child component" pattern and common doubts
Consider a component with frequent state changes, such as updating state inside an onMouseMove callback.
<code>const MovingComponent = () => {
const [state, setState] = useState({ x: 100, y: 100 });
return (
<div
onMouseMove={e => setState({ x: e.clientX, y: e.clientY })}
style={{ left: state.x, top: state.y }}
>
<ChildComponent />
</div>
);
};
</code>When the state updates, React re‑renders the component and all of its child components. In this case, each mouse move triggers a re‑render of MovingComponent , causing ChildComponent to re‑render as well.
If ChildComponent is heavy, frequent re‑rendering can lead to performance problems. Besides React.memo , one solution is to extract the child component and pass it as a separate prop.
<code>const MovingComponent = ({ children }) => {
const [state, setState] = useState({ x: 100, y: 100 });
return (
<div
onMouseMove={e => setState({ x: e.clientX, y: e.clientY })}
style={{ left: state.x, top: state.y }}
>
{/* Now the child will not be re‑rendered */}
{children}
</div>
);
};
const SomeOutsideComponent = () => {
return (
<MovingComponent>
<ChildComponent />
</MovingComponent>
);
};
</code>Here ChildComponent belongs to SomeOutsideComponent , which is the parent of MovingComponent , so it is not affected by the state changes of MovingComponent and does not re‑render on each mouse move.
Question 1: Why do components passed as props not re‑render?
The children prop contains the element <ChildComponent /> created in SomeOutsideComponent . When MovingComponent re‑renders due to state changes, its props remain unchanged, so the element object is not recreated and the child component does not re‑render.
Question 2: Why does a child re‑render when passed as a render function?
<code>const MovingComponent = ({ children }) => {
const [state, setState] = useState({ /* ... */ });
return (
<div>
{/* This will re‑render */}
{children()}
</div>
);
};
const SomeOutsideComponent = () => {
return (
<MovingComponent>
{() => <ChildComponent />}
</MovingComponent>
);
};
</code>Here children is a function; each time MovingComponent re‑renders, it calls the function, creating a new element object and causing ChildComponent to re‑render.
Question 3: Why doesn’t wrapping the parent with React.memo stop the child from re‑rendering, while wrapping the child does?
<code>const MovingComponentMemo = React.memo(MovingComponent);
const SomeOutsideComponent = () => {
const [state, setState] = useState(/* ... */);
return (
<MovingComponentMemo>
<ChildComponent />
</MovingComponentMemo>
);
};
</code>The children prop is a new element object on each render, so even though MovingComponentMemo is memoized, its props change and it re‑renders, which in turn re‑creates the child element.
<code>const ChildComponentMemo = React.memo(ChildComponent);
const SomeOutsideComponent = () => {
const [state, setState] = useState(/* ... */);
return (
<MovingComponent>
<ChildComponentMemo />
</MovingComponent>
);
};
</code>Here the child element is memoized, so when MovingComponent re‑renders, React sees that the child’s element has not changed and skips its re‑render.
Question 4: Why doesn’t memoizing the render function help?
<code>const SomeOutsideComponent = () => {
const [state, setState] = useState(/* ... */);
const child = useCallback(() => <ChildComponent />, []);
return (
<MovingComponent>
{child}
</MovingComponent>
);
};
</code>Although the function child is memoized, it returns a new element object each time it is called, so the child component still re‑renders.
Key concepts:
Creating const child = <Child /> only creates a React element (an immutable object), not a rendered component.
The element is rendered only when it appears in the actual render tree.
Re‑creating the element object triggers a re‑render of the associated component.
Understanding these principles makes it easier to decide when a child component will re‑render and how to use React.memo , useCallback , and useMemo to optimise performance.
This concludes the discussion on React child component re‑rendering.
Code Mala Tang
Read source code together, write articles together, and enjoy spicy hot pot together.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.