Reactjs useMemo and useCallback by examples

2023-03-02

Reactjs useMemo and useCallback by examples

ReactJS is a popular JavaScript library used for building user interfaces. Two important hooks in ReactJS are useMemo and useCallback, which can be used to optimize the performance of your application. useCallback and useMemo are both React hooks that are used for performance optimization, but they serve different purposes.

useMemo is used to memoize the result of a function call, similar to the memo higher-order component. It takes a function and a dependency array, and returns a memoized value that is recalculated only when one of the dependencies changes.

useCallback is used to memoize a function instance, so that the same function instance can be reused across renders, rather than creating a new function on each render. It takes a function and a dependency array, and returns a memoized function instance that is recalculated only when one of the dependencies changes. In other words, useMemo is used to memoize the result of a computation, while useCallback is used to memoize a function itself.

useMemo

useMemo is a React hook that allows you to memoize the result of a function so that it is only recomputed when one of its dependencies change. This can help to optimize the performance of your application by reducing unnecessary re-renders.

Here are two examples of using useMemo:

Example 1: Memoizing Expensive Calculations

Suppose you have a component that displays a list of items. Each item has a complex calculation that needs to be performed before it can be displayed. This calculation is expensive and can take a long time to compute, especially if there are many items in the list.

function Item({ data }) {
  const result = complexCalculation(data);
  return <div>{result}</div>;
}

function List({ items }) {
  return (
    <div>
      {items.map((item) => (
        <Item data={item} />
      ))}
    </div>
  );
}

To avoid performing this calculation unnecessarily, you can memoize the result of complexCalculation using useMemo. This way, the calculation will only be performed when the data prop changes.

function Item({ data }) {
  const result = useMemo(() => {
    return complexCalculation(data);
  }, [data]);
  return <div>{result}</div>;
}

function List({ items }) {
  return (
    <div>
      {items.map((item) => (
        <Item data={item} />
      ))}
    </div>
  );
}

Now, when the data prop for an item changes, the complexCalculation function will be called again, but only for that item. The memoized result will be used for all other items that have not changed, avoiding unnecessary computation.

Example 2: Memoizing a sorted list of items:

Suppose you have a list of items you want to sort , and this list don't change frequently

function MyComponent({ items }) {
  const sortedItems = items.sort((a, b) => a.name.localeCompare(b.name));

  return (
    <ul>
      {sortedItems.map((item) => (
        <li key={item.id}>{item.name}</li>
      ))}
    </ul>
  );
}

To avoid calling sortedItemsunnecessarily, you can memoize it using useMemo. This way, the function will only be executed when the itemsstate changes.

function MyComponent({ items }) {
  const sortedItems = useMemo(() => {
    return items.sort((a, b) => a.name.localeCompare(b.name));
  }, [items]);

  return (
    <ul>
      {sortedItems.map((item) => (
        <li key={item.id}>{item.name}</li>
      ))}
    </ul>
  );
}

In this example, useMemo is used to memoize a sorted list of items. The sort method is an expensive operation, so we want to avoid doing it on every render. Instead, we use useMemo to memoize the sorted list, and only recalculate it when the items dependency changes.

useCallback

useCallback is a React hook that memoizes a function, similar to useMemo, but it is specifically designed for optimizing the performance of function props passed to child components. Here are two examples of using useCallback:

Example 1: Memoizing a Function Prop

Suppose you have a component that takes a function as a prop. This function is expensive and takes a long time to execute. You want to avoid re-creating the function on every render, especially if the prop does not change frequently.

function ExpensiveComponent({ expensiveFunction }) {
  const result = expensiveFunction();
  return <div>{result}</div>;
}

function App() {
  const [value, setValue] = useState(0);

  function expensiveFunction() {
    // Expensive calculation using `value`
  }

  return (
    <div>
      <button onClick={() => setValue(value + 1)}>Increment</button>
      <ExpensiveComponent expensiveFunction={expensiveFunction} />
    </div>
  );
}

To avoid re-creating expensiveFunction on every render, you can memoize it using useCallback.

function ExpensiveComponent({ expensiveFunction }) {
  const result = expensiveFunction();
  return <div>{result}</div>;
}

function App() {
  const [value, setValue] = useState(0);

  const expensiveFunction = useCallback(() => {
    // Expensive calculation using `value`
  }, [value]);

  return (
    <div>
      <button onClick={() => setValue(value + 1)}>Increment</button>
      <ExpensiveComponent expensiveFunction={expensiveFunction} />
    </div>
  );
}

Now, expensiveFunction will only be re-created when the value state changes. Otherwise, the memoized function will be used for all subsequent renders.

Example 2: Preventing Unnecessary Re-renders of Child Components

Suppose you have a parent component that renders several child components. Each child component takes a function as a prop, but the function does not depend on any props or state from the parent component.

function Child({ handleClick }) {
  return <button onClick={handleClick}>Click me</button>;
}

function Parent() {
  const handleClick = () => {
    // Handle click event
  };

  return (
    <div>
      <Child handleClick={handleClick} />
      <Child handleClick={handleClick} />
      <Child handleClick={handleClick} />
    </div>
  );
}

By default, each call to handleClick will create a new function instance, which can cause unnecessary re-renders of the child components. To prevent this, you can memoize handleClick using useCallback.

function Child({ handleClick }) {
  return <button onClick={handleClick}>Click me</button>;
}

function Parent() {
  const handleClick = useCallback(() => {
    // Handle click event
  }, []);

  return (
    <div>
      <Child handleClick={handleClick} />
      <Child handleClick={handleClick} />
      <Child handleClick={handleClick} />
    </div>
  );
}

Now, handleClick will only be created once and shared by all child components. This can improve the performance of your application by reducing unnecessary re-renders.

Advantages of useMemo and useCallback :

1- Improved performance: useMemo can be used to cache the results of expensive calculations, which can improve the performance of your application. useCallback can be used to cache the function instances, which can improve the performance of your application.

2- Avoids unnecessary re-renders: By memoizing the result of a calculation using useMemo , useCallbackyou can prevent the component from re-rendering unnecessarily.

3- Better user experience: Improved performance means a better user experience, as your application will be more responsive and faster.

Disadvantages of useMemo and useCallback :

1- Overuse can lead to complexity: Overusing useMemo and useCallback can lead to complex code that is difficult to maintain.

2- Can cause unnecessary re-renders: Memoized values are stored in memory, so overusing useMemo can lead to an increase in memory usage.

3- Can cause unnecessary re-renders: If you use useMemo on a value that changes frequently, it can cause unnecessary re-renders and decrease performance.

4- May not be necessary in all cases: In some cases, memoizing a function using useCallback may not provide any performance benefits, and may even decrease performance.