I have two components, one receiving another one as a prop. For clarity I will remove all the unnecessary logic and JSX:
function Child() {
useEffect(() => {
console.log("Child rerender");
});
return <div>Child component</div>;
}
function Parent({ children }: { children: React.ReactNode }) {
useEffect(() => {
console.log("Parent rerender");
});
return (
<div>
Parent component
{children}
</div>
);
}
They're both memoized as follows:
const ChildMemo = React.memo(Child);
const ParentMemo = React.memo(Parent);
Now, they're both being rendered in the top level component with a state, again for clarity's sake let's imagine for now it's just a button setting some state:
function App() {
useEffect(() => {
console.log("App rerender");
});
const [count, setCount] = useState(0);
return (
<>
<button onClick={() => setCount(count + 1)}>Update state {count}</button>
<ParentMemo>
<ChildMemo />
</ParentMemo>
</>
);
};
I expect that clicking a button triggers state update leading to just "App rerender" being logged in the console. Yet the <Parent /> component is being rerendered as well. Why does that happen?
FIRST
Looking at your code, you're using
useEffectwithout deps array.So the
parent rerenderedmessage you see in the console is not because it's rerendered, but because you didn't define dependencies array;so every time:
a state changes -> the
ParentMemois executed -> the effect will run -> message is printed to console,but it doesn't mean it's rerendered.
If you want to know if it's rerendered, you must add empty dependencies array:
Without the array,
useEffecthave nothing to compare, so it will assume that you intentionally need to run this effect with everyexecution.SECOND
Since
ChildMemodon't have any state and no props are changed, so there is no need to rerender it, soChildMemois not executed again.But
ParentMemohaschildrenprop, so you can't guarantee it will not be rerendered.To quote from the Docs:
"Wrap a component in memo to get a memoized version of that component. This memoized version of your component will usually not be re-rendered when its parent component is re-rendered as long as its props have not changed. ((But)) React may still re-render it: memoization is a performance optimization, not a guarantee."
To avoid
ParentMemore-execution, you can do an ugly solution, which is to passChildMemoas it's as children:and call it from inside
ParentMemo: