Though React provides better speed and efficiency than other frameworks available we can add some more efficiency to our app by following some simple optimization techniques.
The speed and performance of a web app can be improved if we avoid and prevent unwanted rendering.
Let’s first find out the reasons for re-rendering in parent-child components.
1) The local state of the component is changed/updated.
2) The props passed to the component are changed/updated.
3) Calling forceUpdate in component
The third point is completely dependent on the usecase and scenario where user needs to call forceUpdate and such usecase is not in our control but the first and second point is in our control and can be optimized.
Now let’s see a scenario of unwanted re-rendering
We have two components say CounterController & CounterDisplay.
CounterController has the local state to maintain counter value. And a member function incrementCount which increases the count value. We have passed this function to the onClick event of a button.
CounterDisplay is a simple component for displaying a static string. So CounterDisplay has nothing to do with our CounterController state and should not get affected by its state change.
We have added a console log in our Child component ie. CounterDisplaycomponent to check when it’s re-rendered. So when we click on the button to increment the count state we get “CounterDisplay is re-rendering” in our console.
So this is unwanted re-rendering of our CounterDisplay component. This component may be used at many places in our app and this will affect our app performance.
So how can we avoid this unwanted rendering?
Here comes Memoization to our rescue!
In computing, memoization or memoization 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.
React provides us with few features which internally use the concept of memoization. We can use these features to prevent unwanted rendering.
One of the features is React.memo()
We can wrap our functional component within React.memo().
React.memo() takes two parameters. The component to be memoized and another param is an optional comparison function.(by default does a shallow comparison)
Now, let’s use the React.memo() to wrap our CounterDisplay component and check the console log.
Wow! We can see that our component is not re-rendered each time we click on add button.
Similar to React.memo() is the useMemo hook. A lot of times we compute some complex values in our functional component by performing some data manipulation operations and whenever our component or container which contains this computed value is re-rendered the complex value is computed again.
This affects the performance and can be optimized if we reduce the number of times the complex value is computed. This can be done using the useMemo hook
useMemo hook has a very similar syntax to useEffect. We pass the dependencies in the dependency array. Values on change of which we have to recompute the complex value are been passed in the dependency array.
lets see an example of useMemo
So in this example above our magicalNumber is re-computed always when our CounterDisplay component re-renders. But if we see the value of magicalNumber is only dependent on count prop. So it should be recomputed only when the count prop changes.
Now let’s wrap this magicalNumber in useMemo
If the dependencies are not changed or updated the previously computed value is memoized and used. This reduces the unwanted re-computation and re-rendering of components.
Let’s see the same example with an additional component. We will create a new ResetCounter Component which will reset the counter state.
Let’s define the resetCount function in the parent component CounterController and pass it as prop to the child component ResetCounter. Add a console log within the ResetCounter component to check when its re-rendered.
We can see that on incrementing the count our ResetCounter component is getting re-rendered unnecessarily.
We can avoid this by making use of another feature provided by react ie. useCallback
Our child component is getting re-rendered unnecessarily because whenever the parent component is re-rendered a new member function reference is created which is further passed down to child component. As on each re-render new reference is created it forces the child component to re-render.
useCallback avoids this by memoizing the function reference and the function is re-created only when any one of its dependency is updated.
After wrapping in useCallback we can see that our child component is not re-rendered on count change.
Bingo!! we have optimized our app performance by avoiding unwanted re-rendering.
So following the coding practices we can improve the performance of our application. But as we know everything has its own disadvantages, so we should use these features precisely or else it may led us to unwanted bugs.
Happy Coding… Thank you!