React {children} performance win...
I want to share with you a performance win using the popular react library that you might or might not know about.
So, all of us are using the children prop in React on a daily basis to achieve composition and reusability of the components, but, did you know that you can benefit from it to boost your react performance a ton with a very simple trick? let's see
So, let's get the most popular Counter example.
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<button onClick={() => setCount((count) => count + 1)}>+</button>
<span>count is {count}</span>
<button onClick={() => setCount((count) => count - 1)}>-</button>
</div>
);
}
function App() {
return <Counter />;
}
And, the output is:
hint: here I'm using Vite and this is just the default styling, nothing special here.
Next, let's imagine we have a very slow component that performs an expensive calculation or renders a huge list like the following one.
function HugeList() {
const items = Array.from({ length: 20000 }, () => "Item");
return (
<ul>
{items.map((item, index) => (
<li key={index}>{`${item} ${index}`}</li>
))}
</ul>
);
}
you should adjust this 20000 to a number that your machine can handle, we shouldn't do this anyway but I'm just simulating a kind of expensive operation.
and now add this HugeList component to the Counter component.
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<button onClick={() => setCount((count) => count + 1)}>+</button>
<span>count is {count}</span>
<button onClick={() => setCount((count) => count - 1)}>-</button>
<HugeList />
</div>
);
}
now, what happens is, when you try to increase or decrease the count you will notice that it takes some time to reflect and render the newly updated value.
and this is of course because every time you try to change the counter value it causes the component to rerender and therefore the HugeList component to also rerender as it's a child component of the Counter.
So, now you might already think about using the react memo on the HugeList component but I want to show a different way with the children prop and explains why is this happening.
At this point let's have a look at the profiler and see what happens when changing the Counter value.
here you can notice that the HugeList component will rerender with every change that happens in the Counter component, and this is a normal react behavior.
So, to fix this you can memo the HugeList component, and the code will be something like
const HugeList = memo(function HugeList() {
const items = Array.from({ length: 40000 }, () => "Item");
return (
<ul>
{items.map((item, index) => (
<li key={index}>{`${item} ${index}`}</li>
))}
</ul>
);
});
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<button onClick={() => setCount((count) => count + 1)}>+</button>
<span>count is {count}</span>
<button onClick={() => setCount((count) => count - 1)}>-</button>
<HugeList />
</div>
);
}
function App() {
return <Counter />;
}
so here we wrap a react component in the memo function and it will return a memoized version of that component that will skip rerenders as long as the props of this component didn't change over rerenders. you can read more about the react memo here.
Also, memoization isn't a thing specific to react, it's a concept in software engineering that is an optimization technique used primarily to speed up computer programs by storing the results of expensive function calls and returning the cached result when the same inputs occur again.
and we can still prove this from the profiler again.
领英推荐
now you can notice that the HugeList component didn't rerender again, also the app is responsive again with no delays happening because of the wasted and the expensive operation happens with the HugeList every time something happens with its parent (the Counter component) - again React rules.
Okay, great, but we wanted to prove that you can use the children prop to achieve almost the same thing!
so, let's write the code and then discuss it and analyze it with the profiler.
function HugeList() {
const items = Array.from({ length: 40000 }, () => "Item");
return (
<ul>
{items.map((item, index) => (
<li key={index}>{`${item} ${index}`}</li>
))}
</ul>
);
}
function Counter({ children }: { children: React.ReactNode }) {
const [count, setCount] = useState(0);
return (
<div>
<button onClick={() => setCount((count) => count + 1)}>+</button>
<span>count is {count}</span>
<button onClick={() => setCount((count) => count - 1)}>-</button>
{children}
</div>
);
}
function App() {
return (
<Counter>
<HugeList />
</Counter>
);
}
and the profiler result will be
the profiler again tells us that the HugeList doesn't re-render anymore.
So, here we are accepting the HugeList as a child for the Counter component. but what does this have to do with not rerender the HugeList?
Children are not children, parents are not parents, memoization doesn’t work as it should, life is meaningless, re-renders control our life and nothing can stop them. the article.
let's break down what happened before to compare with the children hack.
so, what happens now is the same thing except the fact that the HugeList component is rendered by the App component, so.
as of now, you might have some questions as I did when I knew about this. and I found this great article going in-depth in explaining how children prop works exactly in react. I will add some of the questions here to motivate you to read the article and adjust them to our example
Q1: Isn't the HugeList component still a child of the Counter component? we are literally rendering it as such.
Q2: what will happen if I passed children as a render function to share some data, so something like {children({ data })}
spoiler: it will re-render, but read the article to know why.
Q3: some other thoughts about combining this with react memo, useMemo, and useCallback hooks. it is great and worth every minute.
References:
Please let me know if you enjoyed it and what can be improved.
Senior Financial analyst
1 年Great effort????keep going????
Front-end Developer at Tangent
1 年Great read, Eslam!! ??? ????? ?????