React - Hooks (useMemo)

React - Hooks (useMemo)

ReactJS hooks are a powerful way to add state and effects to functional components. However, sometimes we need to improve the performance of our components by avoiding unnecessary re-rendering or re-computation of values. In this article, I will explain what useMemo is, how it works, and when to use it.

What is useMemo?

useMemo is a ReactJS hook that lets you memoize a value based on some dependencies. Memoization is a technique of caching the result of a function so that it doesn’t need to be recalculated every time. For example, if you have a function that performs some expensive calculation based on a prop, you can use useMemo to store the result and only update it when the prop changes.

The syntax of useMemo is:

const memoizedValue = useMemo(() => {
  // return the value to be memoized
}, [dependencies]);
        

The first argument is a function that returns the value to be memoized. The second argument is an array of dependencies that determine when the value should be updated. React will compare the dependencies with their previous values using "Object.is" and only call the function if they have changed.

useMemo can help you improve the performance of your components by skipping expensive calculations or avoiding re-rendering of child components.

How does useMemo work?

To understand how useMemo works, let’s look at an example. Suppose we have a component that renders a list of products, and we need to filter them based on a search term. We also have a counter that increments every second. Here is the code without useMemo:

import React, { useState, useEffect } from "react";

const ProductList = ({ products }) => {
  const [search, setSearch] = useState("");
  const [count, setCount] = useState(0);

  // Filter the products based on the search term
  const filteredProducts = products.filter((product) =>
    product.name.toLowerCase().includes(search.toLowerCase())
  );

  // Increment the count every second
  useEffect(() => {
    const interval = setInterval(() => {
      setCount((c) => c + 1);
    }, 1000);
    return () => clearInterval(interval);
  }, []);

  // Handle the input change and update the search state
  const handleChange = (e) => {
    setSearch(e.target.value);
  };

  return (
    <div>
      <div>
        <h2>My Products</h2>
        <input
          type="text"
          placeholder="Search products..."
          value={search}
          onChange={handleChange}
        />
        <ul>
          {filteredProducts.map((product) => (
            <li key={product.id}>{product.name}</li>
          ))}
        </ul>
      </div>
      <hr />
      <div>
        <h2>Counter</h2>
        <p>{count}</p>
      </div>
    </div>
  );
};

export default ProductList;
        

In this code, we have a problem. Every time the count state changes, the component re-renders. This means that the filteredProducts variable is recalculated on every render, even though it only depends on the products and search props. This is inefficient and can cause performance issues if the products array is large or the filter function is complex.

To solve this problem, we can use useMemo to memoize the filteredProducts value. We can wrap the filter function call with useMemo and pass the products and search props as dependencies. This way, the filteredProducts value will only be recalculated when the products or search props change, and not when the count state changes. Here is the code with useMemo:

import React, { useState, useEffect, useMemo } from "react";

const ProductList = ({ products }) => {
  const [search, setSearch] = useState("");
  const [count, setCount] = useState(0);

  // Memoize the filtered products based on the products and search props
  const filteredProducts = useMemo(() => {
    return products.filter((product) =>
      product.name.toLowerCase().includes(search.toLowerCase())
    );
  }, [products, search]);

  // Increment the count every second
  useEffect(() => {
    const interval = setInterval(() => {
      setCount((c) => c + 1);
    }, 1000);
    return () => clearInterval(interval);
  }, []);

  // Handle the input change and update the search state
  const handleChange = (e) => {
    setSearch(e.target.value);
  };

  return (
    <div>
      <div>
        <h2>My Products</h2>
        <input
          type="text"
          placeholder="Search products..."
          value={search}
          onChange={handleChange}
        />
        <ul>
          {filteredProducts.map((product) => (
            <li key={product.id}>{product.name}</li>
          ))}
        </ul>
      </div>
      <hr />
      <div>
        <h2>Counter</h2>
        <p>{count}</p>
      </div>
    </div>
  );
};

export default ProductList;
        

Now, the component is more optimized and the filteredProducts value is only updated when necessary.

When to use useMemo?

useMemo is useful when you have a value that depends on some props or state, and you want to avoid recalculating it on every render. This can improve the performance of your component and prevent unnecessary re-rendering of child components.

However, useMemo is not a magic bullet that will solve all your performance problems. There are some caveats and trade-offs that you need to be aware of when using useMemo:

  • useMemo is a hook, so you can only call it at the top level of your component or your own custom hooks. You can’t call it inside loops, conditions, or nested functions.
  • useMemo is not guaranteed to preserve the memoized value forever. React may decide to throw away the cached value and recalculate it for various reasons, such as low memory or editing the source code. Therefore, you should not rely on useMemo for correctness or consistency, but only for performance optimization.
  • useMemo has a cost of its own. It requires extra memory to store the cached value and extra CPU cycles to compare the dependencies. Therefore, you should not use useMemo for trivial or cheap calculations that don’t affect the performance significantly. You should only use useMemo when the calculation is expensive or the result is used by a large subtree of components.

To summarize, useMemo is a great tool to optimize your components by memoizing values based on dependencies. However, you should use it wisely and only when necessary. You should always measure the performance of your component before and after using useMemo, and make sure that the benefits outweigh the costs.

Conclusion

In this article, I explained what useMemo is, how it works, and when to use it. I hope you learned something new and useful from this article. If you have any questions or feedback, please leave a comment below.


Sources:

要查看或添加评论,请登录

Hassan Fathy的更多文章

  • TypeScript - Types vs. Interfaces

    TypeScript - Types vs. Interfaces

    As TypeScript continues to gain popularity among developers for adding type safety to JavaScript, one of the frequent…

  • React - CSS Modules

    React - CSS Modules

    Introduction: In the bustling world of React development, there's a constant quest for more maintainable and scalable…

  • React - Redux Toolkit with TypeScript

    React - Redux Toolkit with TypeScript

    Introduction As a developer, you’ve probably heard about Redux and its powerful state management capabilities. However,…

  • Typescript - Truthiness

    Typescript - Truthiness

    What is truthiness? Truthiness is a term coined by comedian Stephen Colbert to describe something that feels true, even…

  • React - React Router v6

    React - React Router v6

    React Router is a popular library for declarative routing in React web applications. It allows you to define routes as…

  • TypeScript - Equality

    TypeScript - Equality

    TypeScript’s static type checking adds a layer of complexity and safety to equality comparisons, but the JavaScript…

  • React - Hooks(useRef)

    React - Hooks(useRef)

    React's useRef is a hook, a special function that taps into React features in functional components. It returns a…

    2 条评论
  • TypeScript - typeof vs instanceof

    TypeScript - typeof vs instanceof

    Introduction: Unwrap the mysteries of type assertions in TypeScript with two powerful operators: typeof and instanceof.…

  • React - Hooks(useReducer)

    React - Hooks(useReducer)

    Introduction: In the evolving landscape of React, managing state efficiently is critical for crafting responsive and…

  • TypeScript - Type Guards / Narrowing

    TypeScript - Type Guards / Narrowing

    Introduction: In the dynamic world of web development, TypeScript has emerged as an essential tool for building robust…

社区洞察

其他会员也浏览了