π How React memoization works
You wrapped a component in React.memo, and it still re-renders every time its parent does. Itβs one of the most common surprises in React, and it comes down to what JSX actually creates on every render.
First you should know whats happening when using React and JSX
import React from "react";
function SomeChildren() {
/* ... */
return null; /* ... */
}
function LinkWrapper(props) {
/* ... */
return null; /* ... */
}
function App() {
return (
<LinkWrapper uselink>
<SomeChildren />
</LinkWrapper>
);
}
When compiling JSX, Function App becomes
import React from "react";
function App() {
return React.createElement(
LinkWrapper, // component
{ useLink: true }, // props
React.createElement(SomeChildren), // children
);
}
And at runtime when React calls your function, here is what your function will return
Replaced App return with what kind of data React.createElement is returning.
function App() {
return {
$$typeof: Symbol(react.element),
type: LinkWrapper,
props: {
useLink: true,
children: {
$$typeof: Symbol(react.element),
type: SomeChildren,
props: {},
},
},
};
}
So at every call, React will always get a new definition of your App,
This will trigger to get the definition for all tree of components.
Note: This will not actually render to DOM anything. React just needs to know if anything changed.
Now for example you use React.memo to memoize your LinkWrapper
const LinkWrapper = React.memo((props) => {
return null; /* ... */
});
This will make React to receive previous LinkWrapper return value if props were not changed.
Note: By default it will only shallowly compare complex objects in the props object.
Now letβs come back at our App
function App() {
return (
<LinkWrapper uselink>
<SomeChildren />
</LinkWrapper>
);
}
As I explained above <SomeChildren /> always will return a new React definition.
This means that using React.memo on LinkWrapper will not have any effect.
Because children always will be a new definition.
If you want to also memoize children you will have to do it manually.
function App() {
const memoChildren = React.useMemo(() => <SomeChildren />, []);
return <LinkWrapper uselink>{memoChildren}</LinkWrapper>;
}
This can also be wrote as
function App() {
const memoChildren = React.useMemo(() => <SomeChildren />, []);
return <LinkWrapper uselink children={memoChildren} />;
}
Now memoChildren will always have same value between re-renders
Now LinkWrapper will also see that children did not change,
and will return last memoized value without calling the function again
- Created At
- 3/31/2020
- Updated At
- 9/15/2023
- Published At
- 3/31/2020