"Alternative Approach Before Using Memo in React"
Masood Hussain
Director of Technology | Web3 Innovation | Fullstack | Nextjs | Blockchain | Etherium | Salesforce | Apex | Go-lang
In this post, I aim to introduce two distinct techniques that, despite their simplicity, tend to go unnoticed in terms of their positive impact on rendering performance.
It's worth noting that these techniques complement your existing knowledge and are not meant to substitute for memo or useMemo. However, they are often a valuable starting point to consider.
import { useState } from 'react';
export default function MyComponent() {
let [backgroundColor, setBackgroundColor] = useState('blue');
return (
<div>
<input
value={backgroundColor}
onChange={(e) => setBackgroundColor(e.target.value)}
/>
<p style={{ backgroundColor }}>Greetings, world!</p>
<SlowComponent />
</div>
);
}
function SlowComponent() {
let now = performance.now();
while (performance.now() - now < 100) {
// Simulated delay -- do nothing for 100ms
}
return <p>I am a very time-consuming component.</p>;
}
The problem is that whenever backgroundColor changes inside MyComponent, we will re-render <SlowComponent /> which we’ve artificially delayed to be very slow.
I could easily apply memo() to it and consider the problem solved, but since there are numerous articles already covering that approach, I'd prefer to explore two distinct solutions instead.
Solution 1: Move State Down
export default function MyComponent() {
return (
<>
<MyForm />
<SlowComponent />
</>
);
}
function MyForm() {
let [backgroundColor, setBackgroundColor] = useState('blue');
return (
<>
<input
value={backgroundColor}
onChange={(e) => setBackgroundColor(e.target.value)}
/>
<p style={{ backgroundColor }}>Greetings, world!</p>
</>
);
}
Solution 2: Lift Content Up
The previously mentioned solution is not effective when the state is utilized higher up in the component tree. To illustrate, if we place the backgroundColor state on the parent <div>, it poses a challenge.
领英推荐
export default function MyComponent() {
let [backgroundColor, setBackgroundColor] = useState('blue');
return (
<div style={{ backgroundColor }}>
<input
value={backgroundColor}
onChange={(e) => setBackgroundColor(e.target.value)}
/>
<p style={{ backgroundColor }}>Greetings, world!</p>
<SlowComponent />
</div>
);
}
Below solutions We've divided the MyComponent into two parts. The components and the state variable, which rely on the backgroundColor, have been relocated to the BackgroundHOC component.
The components that are not influenced by the backgroundColor have remained within the MyComponent and are provided to the BackgroundHOC as JSX content, commonly referred to as the children prop.
Whenever the backgroundColor undergoes changes, the BackgroundHOC component re-renders. However, it retains the same children prop it received from the MyComponent during the previous render. Consequently, React does not traverse through that particular subtree.
Consequently, the <SlowComponent /> component remains unaffected and doesn't undergo re-rendering.
export default function MyComponent() {
return (
<BackgroundHOC>
<p>Greetings, world!</p>
<SlowComponent />
</BackgroundHOC>
);
}
function BackgroundHOC({ children }) {
let [backgroundColor, setBackgroundColor] = useState("blue");
return (
<div style={{ backgroundColor }}>
<input
value={backgroundColor}
onChange={(e) => setBackgroundColor(e.target.value)}
/>
{children}
</div>
);
}
Prior to implementing optimizations such as memo or useMemo, it's worth considering whether you can segregate the components that undergo changes from those that remain static.
What's intriguing about these techniques is that their primary focus isn't solely on enhancing performance. Utilizing the children prop to divide components typically simplifies the data flow within your application and minimizes the necessity to pass numerous props down the component tree. Improved performance, in scenarios like this, serves as an additional advantage rather than the primary objective.
Interestingly, this pattern also unlocks further performance advantages in the future.
As an Example, once Server Components are stable and suitable for integration, our BackgroundHOC component might receive its children directly from the server. This means that either the entire <SlowComponent /> component or its individual components could be processed on the server side, and even when a top-level React state update occurs, it would seamlessly bypass those parts on the client side.
That’s something even memo couldn’t do! But again, both approaches are complementary. Don’t neglect moving state down (and lifting content up!)
Hyperautomation | Intelligent Automation | AI, ML | MS in Data Science
1 年Absolutely agree with this article! ?? React's composition model offers such elegant solutions for managing state and component isolation. The idea of utilizing the children prop to separate dynamic and static parts not only improves performance but also enhances code maintainability. And the prospect of integrating Server Components in the future adds another layer of excitement to the mix. Great insights! ?? #React #Optimization #WebDevelopment #CodeSimplicity
Senior Data Engineer|Data Scientist|Gaming|Chess| A.I | Reinforcement Learning|Deep Q Learning|Data Strategy| Data Manager
1 年Great insights on optimizing React components and improving performance! The idea of using the `children` prop for component separation is a fantastic approach that can simplify data flow. Looking forward to the future potential with Server Components. #React #PerformanceOptimization #ReactDevelopment