React.js – performance optimization

React.js – performance optimization

In modern React applications, performance is a very important part. Here we’ll have a look at the couple methods which give you a possibility to optimize the rendering process.

Consider the option when we have a parent and a child component. Let’s try to change the state of the parent component so that the child component is not re-rendering unnecessarily.

 

React.memo()

 

First, take a parent component and child component. In the parent component, add a simple button that will change the state of component. And the child component has a counter which will increase in each render. So we can see how many times the child component is re-rendering.

 


function App(props) {
  const [count, setCount]= useState(0);

  return (
    <div>
      <h1> Count: {count} </h1>
      <button onClick={(e)=>{
        setCount(count + 1);
      }}>
        Plus
      </button>
      <ChildComponent
        someString={'Hello'}
        someNumber={123}
        someBoolean={true}
      />
    </div>
  );
}

let renderCount = 0;

function ChildComponent(props) {
 renderCount += 1;

 return (
   <div>
     <h1>Child component</h1>

     Render count: {renderCount}
   </div>
 );
}

export default ChildComponent;

 

In the first example, we pass some props to the child component. In this case, when we click on the “Plus” button we see that in each click, child component is re-rendering. But indeed, child props were never changed, so it looks like we can optimize this somehow.

For this case, we have React.memo(). Check out the code below.

 

...
export default React.memo(ChildComponent);
...

 

When we wrap child component to the React.memo(), react will compare the primitive props, and if it hasn’t been changed then rendering will not fire. But note(!) this will work only for primitives.

 

useMemo()

 

Another example. We need to pass some array or object as props to the child component.

 

…

  const someArray = [1,2,3]

…

  <ChildComponent
    someString={'Hello'}
    someNumber={123}
    someBoolean={true}
    someArray={someArray}
  />

…

 

In this case, the child component will re-render each time when the user clicks on the “Plus” button.

For handling this case we have a hook useMemo(); This hook gives us a possibility to “cache/memoize” arrays or objects like in the code below.

 

…

  const check = 100;
  const someArray = useMemo(() => {
    return [1,2,3];
  }, [check]);

…

 

Note: just for the sake of examples we use a fixed check value as dependencies

Then everything works fine, the child component is not re-rendering.

 

useCallback()

 

Now let’s check the case when we need to pass some function as a prop.

 

…

  const someFunc = () => {
    return setCount(100);
  };

…

  <ChildComponent
    someString={'Hello'}
    someNumber={123}
    someBoolean={true}
    someArray={someArray}
    someFunc={someFunc}
  />

…

 

To handle this case we can use hook useCallback(); This  hook returns a memoized version of the function

 

…
  const check = 100;

  const someFunc = useCallback(() => {
   return setCount(check);
  }, [check]);
…

 

Note: just for the sake of examples we use a fixed check value as dependencies

Here, someFunc  will not be re-created in every re-render of parent component unless its dependency check changes, so when we click on “Plus” button child component does not re-rendered.

useCallback will check the check variable and if it’s not the same as its previous value it will return the function passed. So React.memo would see a new reference and re-render child component. And  if check variable is the same useCallback would return nothing so React.memo would see a function reference the same as its previous value and cancel the re-render of the child component.

The whole component looks like this:

 

function App(props) {
 const [count, setCount]= useState(0);

 const someArray = useMemo(() => {
   return [1,2,3];
 }, []);

 const someFunc = useCallback(() => {
   return setCount(check);
 }, []);

 return (
   <div>
     <h1> Count: {count} </h1>
     <button onClick={(e)=>{
       setCount(count + 1);
     }}>
       Plus
     </button>

     <ChildComponent
       someString={'Hello'}
       someNumber={123}
       someBoolean={true}
       someArray={someArray}
       someFunc={someFunc}
     />
   </div>
 );
}

 

 

Conclusion

 

This is powerful stuff but according to React documentation, those tricks should be used only when you need to optimize your component. You may rely on those methods as a performance optimization, not as a semantic guarantee. In the future, React may choose to “forget” some previously memoized values and recalculate them on the next render, e.g. to free memory for offscreen components. Write your code so that it still works without these methods — and then add it to optimize performance.