When using React, efficient rendering is crucial for large scale applications to maintain performance.
Here is a simple technique to optimize rendering by using the children
prop.
Try clicking the increment button below to see the problem.
import ChildComponent from "./ChildComponent"
export default function ParentComponent() {
return <ChildComponent />
}
Why this happens
The state count "lags" or pauses between each button click because React re-renders every time count
changes. This includes the rendering of the ExpensiveComponent, causing unnecessary re-renders.
The optimization
To fix this, we can update the props in TypeScript (if you're using it) and replace the direct rendering of ExpensiveComponent
with
the children prop. By doing this, we pass ExpensiveComponent
as children
instead
of rendering it inside the parent directly.
1import { PropsWithChildren } from 'react';
2import { useState } from "react";
3
4export default function ChildComponent({ children }: PropsWithChildren) {
5const [count, setCount] = useState(0);
6 return (
7 <div>
8 <h1>Count: {count}</h1>
9 <button onClick={() => setCount(count + 1)}>Increment</button>
10 {children}
11 </div>
12 );
13}
1import { PropsWithChildren } from 'react';
2import { useState } from "react";
3
4export default function ChildComponent({ children }: PropsWithChildren) {
5const [count, setCount] = useState(0);
6 return (
7 <div>
8 <h1>Count: {count}</h1>
9 <button onClick={() => setCount(count + 1)}>Increment</button>
10 {children}
11 </div>
12 );
13}
1import { PropsWithChildren } from 'react';
2import { useState } from "react";
3
4export default function ChildComponent({ children }: PropsWithChildren) {
5const [count, setCount] = useState(0);
6 return (
7 <div>
8 <h1>Count: {count}</h1>
9 <button onClick={() => setCount(count + 1)}>Increment</button>
10 {children}
11 </div>
12 );
13}
1import { PropsWithChildren } from 'react';
2import { useState } from "react";
3
4export default function ChildComponent({ children }: PropsWithChildren) {
5const [count, setCount] = useState(0);
6 return (
7 <div>
8 <h1>Count: {count}</h1>
9 <button onClick={() => setCount(count + 1)}>Increment</button>
10 {children}
11 </div>
12 );
13}
Next, import the ExpensiveComponent
and pass it in as children
. Now the ExpensiveComponent
is going to
be rendered as children
.
1import ChildComponent from "./ChildComponent";
2import { ExpensiveComponent } from "./ExpensiveComponent";
3
4export default function ParentComponent() {
5 return (
6 <ChildComponent>
7 <ExpensiveComponent />
8 </ChildComponent>
9 );
10}
1import ChildComponent from "./ChildComponent";
2import { ExpensiveComponent } from "./ExpensiveComponent";
3
4export default function ParentComponent() {
5 return (
6 <ChildComponent>
7 <ExpensiveComponent />
8 </ChildComponent>
9 );
10}
1import ChildComponent from "./ChildComponent";
2import { ExpensiveComponent } from "./ExpensiveComponent";
3
4export default function ParentComponent() {
5 return (
6 <ChildComponent>
7 <ExpensiveComponent />
8 </ChildComponent>
9 );
10}
1import ChildComponent from "./ChildComponent";
2import { ExpensiveComponent } from "./ExpensiveComponent";
3
4export default function ParentComponent() {
5 return (
6 <ChildComponent>
7 <ExpensiveComponent />
8 </ChildComponent>
9 );
10}
Why this works
In React, components passed as children
do not automatically re-render when the parent component updates its state.
Final Demo
import ChildComponent from "./ChildComponent";
import { ExpensiveComponent } from "./ExpensiveComponent";
export default function ParentComponent() {
return (
<ChildComponent>
<ExpensiveComponent />
</ChildComponent>
);
}