🐏 How React memoization works

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