I got an interview today, where Interviewer asked me how will you improve performance of your "React Application" - in terms of react only.
He did not want any answer in terms of CSS (concatenation/minifying), JS (concatenation/minifying), Images (Image-Sprites or Using SVGs).
Now, what hint I could get was using:
Now, I have actually no clue what he was talking about? Can someone help me out understanding how these 2 help optimzing in terms of React? Also, is there any other way (specifically, related in terms of React only of how to optimize performance of React-Apps)?
Any help would be appreciated!
This article might help you: A (Mostly) Complete Guide to React Rendering Behavior
From that article you will see that React will compare the before and after virtual DOM tree and look for differences. Whenever it finds one, it will schedule for re-render that whole tree which is under that tree node. I.e: A parent re-renders, all of its children will re-render too. Unless you tell React to re-use some stuff it has rendered previously and that has not changed.
React.memo API reference
The way you can optimize your React app with React.memo() and React.useCallback looks something like this:
1 - An unoptimized React app:
See that when you click the button, App re-renders.
Even though nothing new is being passed to both child components, they also re-render. That's React's default behavior.
function App() {
console.log("Rendering App...");
const [boolean,setBoolean] = React.useState(false);
const someFunction = () => {
console.log("Running someFunction...");
}
return(
<React.Fragment>
<Component1/>
<Component2
someFunction={someFunction}
/>
<button onClick={() => setBoolean((prevState) => !prevState)}>
Re-render App
</button>
</React.Fragment>
);
}
const Component1 = (props) => {
console.log("Rendering Component1...");
return(
<div>I am component 1</div>
);
};
const Component2 = (props) => {
console.log("Rendering Component2...");
return(
<div>I am component 2</div>
);
}
ReactDOM.render(<App/>, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<div id="root"/>
2 - React App optimized with React.memo()
React.memo() tells React something like that: if the new render will call that memoized component with the exact same props from the last render, don't render it again. 'Cause same props will produce same result anyway.
You will see that Component1 has stopped re-rendering, since it keeps being called with empty props. But Component2 keeps re-rendering. That's because every time you re-render App, a new someFunction variable with a new reference for the function is being created, thus, is also being changed.
And React only does a shallow compare. It does not look into the someFunction code. It only compares the reference for that variable (or a value, in case of a primitive). If any of the props is different than last render, it will re-render.
function App() {
console.log("Rendering App...");
const [boolean,setBoolean] = React.useState(false);
const someFunction = () => {
console.log("Running someFunction...");
}
return(
<React.Fragment>
<Component1/>
<Component2
someFunction={someFunction}
/>
<button onClick={() => setBoolean((prevState) => !prevState)}>
Re-render App
</button>
</React.Fragment>
);
}
const Component1 = React.memo((props) => {
console.log("Rendering Component1...");
return(
<div>I am component 1</div>
);
});
const Component2 = React.memo((props) => {
console.log("Rendering Component2...");
return(
<div>I am component 2</div>
);
});
ReactDOM.render(<App/>, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<div id="root"/>
3 - Optimized with React.memo() and React.useCallback()
You'll see now that only App re-renders. Because useCallback "tells" React to keep the function reference from the very first render and only change it if necessary. In this example, it will never change, because we've passed an empty array for useCallback.
Since it won't change, Component2 will no longer be re-rendered every time, 'cause it will receive always the same props.
useCallback API reference
function App() {
console.log("Rendering App...");
const [boolean,setBoolean] = React.useState(false);
const someFunction = React.useCallback(() => {
console.log("Running someFunction...");
},[]);
return(
<React.Fragment>
<Component1/>
<Component2
someFunction={someFunction}
/>
<button onClick={() => setBoolean((prevState) => !prevState)}>
Re-render App
</button>
</React.Fragment>
);
}
const Component1 = React.memo((props) => {
console.log("Rendering Component1...");
return(
<div>I am component 1</div>
);
});
const Component2 = React.memo((props) => {
console.log("Rendering Component2...");
return(
<div>I am component 2</div>
);
});
ReactDOM.render(<App/>, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<div id="root"/>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With