0
0
Lập trình
Flame Kris
Flame Krisbacodekiller

Các lỗi thường gặp khi sử dụng React Hooks và cách khắc phục

Đăng vào 6 tháng trước

• 6 phút đọc

📋 Mục lục

  1. useState – Nền tảng của trạng thái
  2. useEffect – Hiệu ứng phụ và vòng đời
  3. useContext – Chia sẻ trạng thái mà không cần props
  4. useReducer – Logic trạng thái phức tạp và có thể dự đoán
  5. useRef – Tham chiếu, lưu trữ và thoát
  6. useMemouseCallback – Tối ưu hóa hiệu suất
  7. Custom Hooks – Tạo logic tái sử dụng
  8. Lỗi thường gặp và giải pháp

Phần này tóm tắt các vấn đề phổ biến mà bạn có thể gặp phải khi làm việc với Hooks, nhiều trong số đó là nguyên nhân gốc rễ gây ra các lỗi khó phát hiện.

1. Vi phạm quy tắc của hooks

React rất nghiêm ngặt về hai quy tắc này vì một lý do quan trọng: React phụ thuộc vào thứ tự mà các Hooks được gọi để liên kết trạng thái và các dữ liệu khác với sợi (fiber) của thành phần đúng.

  • ❌ Không gọi Hooks bên trong vòng lặp, điều kiện hoặc các hàm lồng nhau.
  • ❌ Không gọi Hooks từ các hàm JavaScript thông thường. (Chỉ từ các thành phần React hoặc Custom Hooks).
javascript Copy
// SAI ❌
if (condicion) {
  // Thứ tự gọi thay đổi, React bị mất.
  const [value, setValue] = useState(0);
}

Hệ quả: Lỗi khó hiểu, trạng thái bị trộn lẫn giữa các hooks khác nhau và hành vi không thể đoán trước.
Giải pháp: Luôn gọi các hooks ở cấp độ cao nhất của thành phần. Nếu bạn cần logic điều kiện, hãy đặt nó bên trong hook.

javascript Copy
// ĐÚNG ✅
useEffect(() => {
  if (condicion) {
    // Logic điều kiện nằm *bên trong* hook.
    // Hook được gọi không điều kiện.
  }
}, [condicion]);

2. Trạng thái "cũ" (Stale State) do closures

Đây là khái niệm khó nhất nhưng cũng quan trọng nhất để hiểu. Khi một thành phần được render, nó "nắm bắt" các giá trị của props và trạng thái của render cụ thể đó trong các hàm của nó (closures).

javascript Copy
// VẤN ĐỀ 😬
function Counter() {
  const [count, setCount] = useState(0);

  const showAlert = () => {
    // Hàm này "nhớ" giá trị của `count`
    // từ render mà nó được tạo ra.
    alert('Giá trị đếm là ' + count);
  };

  return (
    <div>
      <p>Đếm: {count}</p>
      <button onClick={() => setCount(count + 1)}>+1</button>
      <button onClick={showAlert}>Hiện Thông Báo</button>
    </div>
  );
}

Nếu bạn nhấp vào "+1" nhiều lần và sau đó nhấp vào "Hiện Thông Báo", thông báo sẽ hiển thị giá trị của count mà tồn tại khi nút được render, không phải giá trị hiện tại.

Giải pháp 1: Cập nhật theo cách chức năng
Nếu trạng thái mới phụ thuộc vào trạng thái cũ, hãy sử dụng hình thức chức năng của setState. React đảm bảo rằng bạn luôn nhận được phiên bản mới nhất của trạng thái.

javascript Copy
// Lý tưởng cho các sự kiện hoặc khoảng thời gian
setCount(prevCount => prevCount + 1);

Giải pháp 2: Mảng phụ thuộc
Đối với useEffectuseCallback, hãy đảm bảo rằng tất cả các biến bên ngoài bạn sử dụng đều nằm trong mảng phụ thuộc. Điều này đảm bảo rằng hàm sẽ "tươi mới" với các giá trị mới khi chúng thay đổi.

3. Mảng phụ thuộc gây tranh cãi

  • Bỏ qua phụ thuộc: Gây ra "stale state" vì hook sử dụng phiên bản cũ của một hàm hoặc biến. Linter của React (eslint-plugin-react-hooks) là người bạn tốt nhất của bạn để phát hiện điều này. Đừng bỏ qua nó!
  • Phụ thuộc thay đổi quá nhiều: Nếu bạn bao gồm một đối tượng hoặc một hàm được định nghĩa lại trong mỗi lần render, hiệu ứng sẽ được thực thi liên tục.
    • Giải pháp: Đối với các hàm, hãy sử dụng useCallback. Đối với các đối tượng, hãy sử dụng useMemo. Đối với các phụ thuộc của setState từ một useState, bạn có thể bỏ qua chúng, vì React đảm bảo rằng chúng ổn định.

4. Lạm dụng tối ưu hóa

Đừng bao bọc tất cả trong useMemouseCallback theo mặc định.

  • Chi phí: Những hooks này không miễn phí. Chúng làm tăng độ phức tạp và tiêu tốn bộ nhớ.
  • Khi nào nên sử dụng chúng:
    1. Khi bạn truyền props cho một thành phần con được bao bọc trong React.memo.
    2. Đối với các tính toán thực sự nặng nề đang làm chậm thành phần của bạn.
    3. Để ổn định một phụ thuộc của hook khác (như useEffect).

Quy tắc vàng: Đừng tối ưu hóa cho đến khi bạn đo lường. Sử dụng Profiler của React DevTools để tìm các nút thắt thực sự.


Thực hành tốt nhất

  • Hiểu rõ cách hoạt động của hooks: Hãy đọc tài liệu chính thức về Hooks để nắm rõ cách thức hoạt động và quy tắc mà bạn cần tuân thủ.
  • Sử dụng linter: Cài đặt eslint-plugin-react-hooks để phát hiện sớm các lỗi liên quan đến hooks.
  • Tạo Custom Hooks: Tái sử dụng logic để giữ cho mã của bạn sạch sẽ và dễ bảo trì.

Những cạm bẫy thường gặp

  • Không hiểu rõ về closures: Điều này có thể dẫn đến việc không cập nhật đúng các giá trị trong các hàm callback.
  • Lạm dụng useEffect: Thiếu mảng phụ thuộc sẽ làm cho hiệu ứng chạy không cần thiết.

Mẹo hiệu suất

  • Sử dụng React.memo: Để ngăn chặn render lại không cần thiết cho các thành phần con.
  • Tối ưu hóa các hàm callback: Sử dụng useCallback hợp lý để cải thiện hiệu suất.

Khắc phục sự cố

  • Lỗi không phản hồi: Kiểm tra xem các hooks có được gọi theo đúng thứ tự hay không, và mảng phụ thuộc có đầy đủ chưa.
  • Giá trị hiển thị không chính xác: Kiểm tra các closures và đảm bảo rằng bạn đang sử dụng setState theo cách chức năng khi cần thiết.

Tài nguyên bổ sung

Để tiếp tục tìm hiểu sâu hơn, đây là những nơi tốt nhất mà bạn có thể tìm đến.

  1. Tài liệu chính thức của React (tiếng Việt)

    • Giới thiệu về Hooks: Nguồn thông tin trung thực. Luôn được cập nhật và có giải thích tuyệt vời.
    • Quy tắc của Hooks: Đọc là bắt buộc.
  2. Bài viết và hướng dẫn hình ảnh

    • Hướng dẫn hoàn chỉnh về useEffect: Dù bằng tiếng Anh, đây là giải thích tốt nhất về useEffect và mô hình tư duy của nó.
    • Hình dung quá trình render của React: Giúp bạn hiểu tại sao và khi nào các thành phần được re-render.
  3. Công cụ

    • Profiler của React DevTools: Một tiện ích mở rộng cho trình duyệt cần thiết để tìm các vấn đề về hiệu suất và hiểu nguyên nhân gây ra việc re-render.
    • ESLint Plugin cho React Hooks: Được bao gồm mặc định trong Create React App. Nó sẽ cảnh báo bạn về các lỗi thường gặp, đặc biệt là với các mảng phụ thuộc.

Chúc bạn lập trình vui vẻ! 🚀

Gợi ý câu hỏi phỏng vấn
Không có dữ liệu

Không có dữ liệu

Bài viết được đề xuất
Bài viết cùng tác giả

Bình luận

Chưa có bình luận nào

Chưa có bình luận nào