How to Optimize React Performance with useMemo

How to Optimize React Performance with useMemo



Brief on how useMemo works

A memoized value is returned by the useMemo Hook in React.

Memoization is similar to saving a value from being recalculated by caching it.

The only time the useMemo Hook runs is when one of its dependencies changes.

This can help us to improve it's performance 



Usememo syntax

useMemo(calculateValue, dependencies)

How to improve Performance by using useMemo

Using the useMemo Hook, you can stop expensive, resource-intensive functions from running all the time.

We have an expensive function that runs on every render in this example.

There will be a delay in execution when you change the count or add a to-do.

A function that performs poorly. Every render calls the expensiveOperation function:


import { useState } from "react";

import ReactDOM from "react-dom/client";


const App = () => {

  const [count, setCount] = useState(0);

  const [todos, setTodos] = useState([]);

  const calculation = expensiveOperation(count);


  const increment = () => {

    setCount((c) => c + 1);

  };

  const addTodo = () => {

    setTodos((t) => [...t, "New Todo"]);

  };


  return (

    <div>

      <div>

        <h2>My Todos</h2>

        {todos.map((todo, index) => {

          return <p key={index}>{todo}</p>;

        })}

        <button onClick={addTodo}>Add Todo</button>

      </div>

      <hr />

      <div>

        Count: {count}

        <button onClick={increment}>+</button>

        <h2>Expensive Calculation</h2>

        {calculation}

      </div>

    </div>

  );

};


const expensiveOperation = (num) => {

  console.log("Calculating...");

  for (let i = 0; i < 2000000000; i++) {

    num += 1;

  }

  return num;

};


const root = ReactDOM.createRoot(document.getElementById('root'));

root.render(<App />);


Use useMemo


Caching return values like this is also known as memoization, which is why this Hook is called.


To use Memo, two things must be passed:
  • A calculation function that takes no arguments, like () =>, and returns what you wanted to calculate.
  • A list of dependencies including every value within your component that’s used inside your calculation.
Note:-
UseMemo should only be used to improve performance. Find the underlying issue and address it first if your code does not function properly without it. Then you can boost performance by adding useMemo.

We can memoize the expensiveOperation function with the help of the useMemo Hook to address this performance issue. The function will only run when necessary as a result of this.


Using useMemo, we can wrap the pricey function call.


For declaring dependencies, the useMemoHook accepts a second parameter. When the expensive function's dependencies have changed, it will only run.


The expensive function will only run in the following example when the count changes, not when todos are added.


import { useState, useMemo } from "react";

import ReactDOM from "react-dom/client";


const App = () => {

  const [count, setCount] = useState(0);

  const [todos, setTodos] = useState([]);

  const calculation = useMemo(() => expensiveOperation(count), [count]);


  const increment = () => {

    setCount((c) => c + 1);

  };

  const addTodo = () => {

    setTodos((t) => [...t, "New Todo"]);

  };


  return (

    <div>

      <div>

        <h2>My Todos</h2>

        {todos.map((todo, index) => {

          return <p key={index}>{todo}</p>;

        })}

        <button onClick={addTodo}>Add Todo</button>

      </div>

      <hr />

      <div>

        Count: {count}

        <button onClick={increment}>+</button>

        <h2>Expensive Calculation</h2>

        {calculation}

      </div>

    </div>

  );

};


const expensiveOperation = (num) => {

  console.log("Calculating...");

  for (let i = 0; i < 2000000000; i++) {

    num += 1;

  }

  return num;

};


const root = ReactDOM.createRoot(document.getElementById('root'));

root.render(<App />);


Caveats

  • Because useMemo is a Hook, you can only use it in your own Hooks or at the top level of your component. It cannot be called within conditions or loops. Move the state into a new component that has been extracted in case you require that.
  • React will call your calculation function twice in Strict Mode to assist you in discovering accidental impurities. This behavior only affects development and has no effect on production. Your component's logic shouldn't be affected if your calculation function is pure, as it should be. One of the calls' outcomes will be ignored.
  • Unless there is a specific reason to do so, React will not discard the cached value. React, for instance, discards the cache when editing a component file during development. If your component suspends during the initial mount, React will discard the cache in both development and production. If React adds built-in support for virtualized lists in the future, it would make sense to throw away the cache for items that scroll out of the virtualized table viewport. Additionally, React may add additional features that take advantage of throwing away the cache in the future. If you only use useMemo to improve performance, this should meet your expectations. If not, a reference or a state variable might be more suitable.

Troubleshooting

Troubleshooting In Strict Mode, React will call some of your functions twice rather than once: My calculation runs twice on every re-render

function TodoList(todos, tab) // For each render, this component function will run twice.

  const visibleTodos = useMemo(() => // If any of the dependencies change, this calculation will run twice.
    return (todos, tab) filterTodos;
  , tab, [todos]);

  // This is normal and shouldn't cause your code to break.

You can maintain component purity with this development-only behavior. React takes into account the outcome of one call and disregards the outcome of the other. This shouldn't affect your logic as long as your component and calculation functions are pure. However, if they are impure by accident, this helps you identify the errors and correct them.

Additionally, check out the guides for updating arrays and objects without mutation.

The object that my useMemo call is supposed to return is undefined.


This code is ineffective:

 // You can't return an object from an arrow function with () => {
  const searchOption = useMemo(() => {
    matchMode: 'whole-word',
    text: text
  }, [text]);


Write an explicit return statement to prevent this error:

//  This works and is explicit
const searchOption = useMemo(() => {
return {
matchMode: 'whole-word',
text: text
};
}, [text]);


Comments

Popular posts from this blog

Tips for Optimizing React Native Flatlist Performance

Leveraging React Native Vector Icons in Your Projects

What is react native vision camera and how to install it in react native project?