Giới thiệu

Trong các ứng dụng React, việc render lại các component không cần thiết có thể gây giảm hiệu năng, đặc biệt khi giao diện phức tạp hoặc danh sách dữ liệu lớn. Bài viết này sẽ phân tích nguyên nhân gây re‑render và đưa ra giải pháp thực tiễn bằng useMemoReact.memo, đồng thời minh hoạ cách áp dụng trong dự án TypeScript.

Hiểu vấn đề re‑render

Khi một component cha thay đổi state hoặc props, React sẽ tự động render lại toàn bộ cây con, ngay cả khi các props của component con không thay đổi. Điều này dẫn tới việc thực thi lại các hàm tính toán, tạo ra các đối tượng mới và gây tốn tài nguyên.

Giải pháp useMemo

useMemo cho phép ghi nhớ (memoize) kết quả của một hàm tính toán nặng, chỉ tái tính khi các phụ thuộc thay đổi. Điều này giúp tránh việc tạo lại dữ liệu mỗi lần component cha render.

Ví dụ thực tế

import React, { useState, useMemo } from 'react';

// Hàm tạo danh sách ngẫu nhiên nặng
function generateItems(count) {
  const items = [];
  for (let i = 0; i < count; i++) {
    items.push({ id: i, name: `Item ${i}` });
  }
  return items;
}

function ItemList({ items }) {
  console.log('Render ItemList');
  return (
    <ul>
      {items.map(item => (
        <li key={item.id}>{item.name}</li>
      ))}
    </ul>
  );
}

function Parent() {
  const [count, setCount] = useState(0);
  // Memoize danh sách 1000 phần tử, chỉ tạo một lần
  const items = useMemo(() => generateItems(1000), []);

  return (
    <div>
      <button onClick={() => setCount(c => c + 1)}>Increment {count}</button>
      <ItemList items={items} />
    </div>
  );
}

export default Parent;

Trong ví dụ trên, generateItems chỉ được gọi một lần nhờ useMemo. Khi nhấn nút tăng count, Parent render lại, nhưng ItemList không phải tính lại danh sách, giảm đáng kể thời gian render.

Giải pháp React.memo

React.memo là một Higher‑Order Component (HOC) giúp ngăn component con render lại nếu props không thay đổi. Khi kết hợp với useMemo, chúng ta có thể tối ưu cả dữ liệu và component.

Ví dụ thực tế

import React, { useState, useMemo } from 'react';

const ItemList = React.memo(function ItemList({ items }) {
  console.log('Render ItemList (memo)');
  return (
    <ul>
      {items.map(item => (
        <li key={item.id}>{item.name}</li>
      ))}
    </ul>
  );
});

function Parent() {
  const [count, setCount] = useState(0);
  const items = useMemo(() => generateItems(1000), []);

  return (
    <div>
      <button onClick={() => setCount(c => c + 1)}>Increment {count}</button>
      <ItemList items={items} />
    </div>
  );
}

export default Parent;

Với React.memo, ngay khi Parent thay đổi count, ItemList sẽ không re‑render vì props items không thay đổi, nhờ đó giảm số lần render không cần thiết.

Kết hợp useMemoReact.memo trong TypeScript

Trong dự án TypeScript, chúng ta nên khai báo kiểu cho props để tận dụng lợi thế của hệ thống kiểu. Dưới đây là một ví dụ hoàn chỉnh:

Một ví dụ hoàn chỉnh

import React, { useState, useMemo } from 'react';

type Item = {
  id: number;
  name: string;
};

type ItemListProps = {
  items: Item[];
};

const ItemList = React.memo(function ItemList({ items }: ItemListProps) {
  console.log('Render ItemList (TS)');
  return (
    <ul>
      {items.map(item => (
        <li key={item.id}>{item.name}</li>
      ))}
    </ul>
  );
});

function generateItems(count: number): Item[] {
  const items: Item[] = [];
  for (let i = 0; i < count; i++) {
    items.push({ id: i, name: `Item ${i}` });
  }
  return items;
}

function Parent() {
  const [count, setCount] = useState(0);
  const items = useMemo(() => generateItems(2000), []);

  return (
    <div>
      <button onClick={() => setCount(c => c + 1)}>Increment {count}</button>
      <ItemList items={items} />
    </div>
  );
}

export default Parent;

Nhờ việc khai báo kiểu ItemItemListProps, TypeScript giúp phát hiện lỗi sớm, đồng thời useMemoReact.memo giảm thiểu re‑render, mang lại trải nghiệm người dùng mượt mà hơn.

Kết luận

Việc sử dụng useMemo để ghi nhớ dữ liệu nặng và React.memo để ngăn component không thay đổi là hai kỹ thuật tối ưu quan trọng trong React. Khi kết hợp với TypeScript, chúng không chỉ cải thiện hiệu năng mà còn tăng tính ổn định của code.

Để nắm vững các kỹ thuật này và nhiều kiến thức thực tiễn khác, Tham khảo khóa học "Lập trình Front-End với ReactJS + TypeScript" tại đây.