Trong bài trước chúng ta đã cùng nhau tìm hiểu về hàm useCallback trong bài này Dũng sẽ cùng các bạn tìm hiểu nốt về hàm useMemo nhé.

Vấn đề

Hãy nói chúng ta có một hàm tính toán rất phức tạp kiểu thế này:

const doCalculate = (num: number) => {
  console.log("Calculating...");
  for (let i = 0; i < 1000000000; i++) {
    num += 1;
  }
  return num;
};

Hàm này chạy một vòng lặp 1 tỷ lần để cộng tổng, hàm này trong thực tế sẽ không dùng đến, tuy nhiên vì mục đích demo nên mình sẽ sử dụng nó.
Và mã nguồn của App.tsx sẽ thay đổi như sau:

import { useState } from 'react'
import reactLogo from './assets/react.svg'
import viteLogo from '/vite.svg'
import './App.css'

const doCalculate = (num: number) => {
  console.log("Calculating...");
  for (let i = 0; i < 1000000000; i++) {
    num += 1;
  }
  return num;
};

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

  const calculation = doCalculate(count);

  return (
    <>
      <div>
        <a href="https://vitejs.dev" target="_blank">
          <img src={viteLogo} className="logo" alt="Vite logo" />
        </a>
        <a href="https://react.dev" target="_blank">
          <img src={reactLogo} className="logo react" alt="React logo" />
        </a>
      </div>
      <h1>Vite + React</h1>
      <div className="card">
        <button onClick={() => setCount((count) => count + 1)}>
          count is {count}
        </button>
        <p>
          calculation: {calculation}
        </p>
        <p>
          Edit <code>src/App.tsx</code> and save to test HMR
        </p>
      </div>
      <p className="read-the-docs">
        Click on the Vite and React logos to learn more
      </p>
    </>
  )
}

export default App

Chúng ta kỳ vọng rằng mỗi lần chúng ta nhấn nút count is thì count is sẽ tăng và calculation sẽ không bị tính toán lại vì chúng ta đã khai báo kiểu biến không thay đổi giá trị const calculation = doCalculate(count);
Tuy nhiên thực tế kết quả sẽ như sau:

Như bạn có thể thấy ở phần console, khi chúng ta nhấn vào nút count is thì vẫn có log Calculating... được in ra điều này có nghĩa là calculation vẫn được tính toán lại và hàm doCalculate vẫn được gọi.

Sử dụng useMemo

Để giải quyết vấn đề trên chúng ta có thể sử dụng useMemo và mã nguồn của App.tsx như sau:

import { useMemo, useState } from 'react'
import reactLogo from './assets/react.svg'
import viteLogo from '/vite.svg'
import './App.css'

const doCalculate = (num: number) => {
  console.log("Calculating...");
  for (let i = 0; i < 1000000000; i++) {
    num += 1;
  }
  return num;
};

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

  const calculation = useMemo(() => {
    return doCalculate(count)
  }, []);

  return (
    <>
      <div>
        <a href="https://vitejs.dev" target="_blank">
          <img src={viteLogo} className="logo" alt="Vite logo" />
        </a>
        <a href="https://react.dev" target="_blank">
          <img src={reactLogo} className="logo react" alt="React logo" />
        </a>
      </div>
      <h1>Vite + React</h1>
      <div className="card">
        <button onClick={() => setCount((count) => count + 1)}>
          count is {count}
        </button>
        <p>
          calculation: {calculation}
        </p>
        <p>
          Edit <code>src/App.tsx</code> and save to test HMR
        </p>
      </div>
      <p className="read-the-docs">
        Click on the Vite and React logos to learn more
      </p>
    </>
  )
}

export default App

Ở đây chúng chỉ thay đổi đoạn mã từ:

 const calculation = doCalculate(count);

Sang:

  const calculation = useMemo(() => {
    return doCalculate(count);
  }, []);

Tại sao useMemo lại ngăn chặn được việc tính toán lại? Đó là do nó có khả năng lưu giữ lại kết quả tính toán cho đến khi nào các biến phụ thuộc của nó bị thay đổi. Ví dụ chúng ta có thể bổ sung biến count vào danh sách phụ thuộc như sau:

  const calculation = useMemo(() => {
    return doCalculate(count)
  }, [count]);

Trong các lần render tiếp theo, React sẽ so sánh các phụ thuộc với các phụ thuộc bạn đã truyền trong lần render trước. Nếu không có phụ thuộc nào thay đổi (sử dụng hàm Object.is) thì hàm doCalculate sẽ được gọi tính toán lại.

Tổng kết

Như vậy chúng ta đã cùng nhau tìm hiểu về hàm useMemo đây là hàm cuối cùng trong danh sách hàm của React Hook mà mình muốn giới thiệu đến các bạn.


Cám ơn bạn đã quan tâm đến bài viết này. Để nhận được thêm các kiến thức bổ ích bạn có thể:

  1. Đọc các bài viết của TechMaster trên facebook: https://www.facebook.com/techmastervn
  2. Xem các video của TechMaster qua Youtube: https://www.youtube.com/@TechMasterVietnam nếu bạn thấy video/bài viết hay bạn có thể theo dõi kênh của TechMaster để nhận được thông báo về các video mới nhất nhé.
  3. Chat với techmaster qua Discord: https://discord.gg/yQjRTFXb7a