0
0
Lập trình
TT

`useRef` – Giới thiệu về tham chiếu và giá trị bền vững

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

• 5 phút đọc

📋 Mục lục

  1. useState – Cơ sở của state
  2. useEffect – Tác dụng phụ và vòng đời
  3. useContext – Chia sẻ state mà không cần props
  4. useReducer – Logic state 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

useRef là gì và tại sao cần sử dụng?

useRef là một hook linh hoạt với hai trường hợp sử dụng chính, mặc dù có vẻ khác nhau, nhưng đều dựa trên cùng một nguyên tắc: cung cấp một đối tượng có thể thay đổi (.current) mà tồn tại suốt vòng đời của component mà không gây ra re-render khi giá trị thay đổi.

  1. Truy cập vào các phần tử DOM: Đây là "lối thoát" để tương tác trực tiếp với một phần tử DOM, chẳng hạn như để quản lý tiêu điểm, đo kích thước hoặc tích hợp các thư viện bên thứ ba (như D3.js).
  2. Lưu trữ "biến phiên bản": Giúp bạn giữ một giá trị có thể thay đổi mà không cần kích hoạt một render mới. Nó giống như việc có một biến phiên bản trong một component class. Rất thích hợp để lưu ID của các timer, bộ đếm nội bộ, hoặc bất kỳ giá trị nào bạn cần tồn tại giữa các lần render.

Cú pháp và các đặc điểm chính

javascript Copy
const miRef = useRef(valorInicial);
  • useRef trả về một đối tượng với một thuộc tính duy nhất: current.
  • miRef.current được khởi tạo với valorInicial.
  • Bạn có thể thay đổi miRef.current trực tiếp: miRef.current = nuevoValor.
  • Quan trọng: Thay đổi miRef.current KHÔNG kích hoạt một re-render.

Ví dụ thực tiễn chi tiết: input với tiêu điểm và bộ đếm render

Ví dụ này cho thấy cả hai cách sử dụng của useRef: một cho DOM và một cho giá trị bền vững.

javascript Copy
import React, { useState, useEffect, useRef } from 'react';

function FocusCounter() {
  const [inputValue, setInputValue] = useState('');

  // Trường hợp sử dụng 1: Tham chiếu đến DOM
  const inputRef = useRef(null);

  // Trường hợp sử dụng 2: Biến bền vững
  const renderCount = useRef(0);

  useEffect(() => {
    // Thực hiện sau mỗi lần render
    renderCount.current = renderCount.current + 1;
  });

  const handleFocus = () => {
    // Truy cập vào phần tử DOM trực tiếp
    inputRef.current.focus();
  };

  return (
    <div>
      <h3>Ví dụ về useRef</h3>
      <input
        ref={inputRef} // Kết nối ref với phần tử input
        type="text"
        value={inputValue}
        onChange={(e) => setInputValue(e.target.value)}
        placeholder="Nhập gì đó..."
      />
      <button onClick={handleFocus}>Đặt tiêu điểm vào Input</button>

      <p>Giá trị của input là: "{inputValue}"</p>
      <p>Component này đã được render {renderCount.current} lần.</p>
      <p>(Lưu ý: bộ đếm render không gây ra re-renders bởi chính nó)</p>
    </div>
  );
}

Trong ví dụ này:

  • inputRef cho phép chúng ta truy cập trực tiếp vào <input>, cho phép chúng ta gọi hàm .focus().
  • renderCount theo dõi số lần render. Nếu chúng ta sử dụng useState cho điều này, mỗi lần cập nhật bộ đếm sẽ gây ra một render khác, tạo ra vòng lặp vô hạn. Với useRef, chúng ta có thể cập nhật giá trị mà không có tác dụng phụ.

✅ Thực hành tốt và các mẫu phổ biến

  1. Khi nào sử dụng useRef so với useState:

    • Sử dụng useState nếu sự thay đổi trong giá trị cần phải hiển thị trong UI.
    • Sử dụng useRef nếu bạn cần giá trị tồn tại giữa các lần render nhưng không muốn sự thay đổi gây ra một render mới.
  2. Lưu trữ giá trị trước đó của props hoặc state: Một mẫu rất hữu ích là kết hợp useRefuseEffect để "nhớ" một giá trị từ render trước đó.

    javascript Copy
    function Componente({ valor }) {
      const valorPrevioRef = useRef();
      useEffect(() => {
        valorPrevioRef.current = valor;
      }, [valor]); // Cập nhật sau khi render hoàn tất
      const valorPrevio = valorPrevioRef.current;
      // ...
    }
  3. Quản lý Timers: useRef rất lý tưởng để lưu ID của setInterval hoặc setTimeout để có thể dọn dẹp sau này.

🚨 Lỗi thường gặp và cách tránh

  • Lỗi: Truy cập vào ref.current quá sớm.

    javascript Copy
    // SAI ❌
    function MiComponente() {
      const miRef = useRef(null);
      // Ở đây, trong lần render đầu tiên, miRef.current là `null`
      // vì DOM vẫn chưa tồn tại. Điều này sẽ gây lỗi.
      miRef.current.focus();
    
      return <input ref={miRef} />;
    }
    • Giải pháp: Truy cập vào ref bên trong một useEffect (được thực hiện sau khi render) hoặc trong các trình xử lý sự kiện (được thực hiện qua tương tác của người dùng).
    javascript Copy
    // ĐÚNG ✅
    useEffect(() => {
      // Đối với lần render đầu tiên, điều này sẽ thực hiện sau khi input tồn tại.
      miRef.current.focus();
    }, []); // Mảng rỗng để chỉ thực hiện một lần
  • Lỗi: Hiển thị giá trị của một ref trong UI và mong đợi rằng nó sẽ được cập nhật.

    • Giải pháp: Nếu bạn cần sự thay đổi được phản ánh trong render, giá trị đó phải nằm trong state (useState). Nhớ rằng: useRef không thông báo cho React khi nó thay đổi.

🚀 Thử thách thực hành

  1. Trình phát Video: Tạo một component hiển thị video (<video>). Sử dụng useRef để lấy tham chiếu đến phần tử và thêm các nút cho "Phát", "Tạm dừng" và "Dừng".
  2. Đồng hồ Bấm Giây Chính Xác: Tạo một đồng hồ bấm giây sử dụng setInterval. Lưu ID của interval trong một ref để có thể dừng và khởi động lại đúng cách.
  3. Cuộn đến một Phần Tử: Tạo một danh sách dài các phần tử. Thêm các nút ở đầu để khi nhấp vào, cuộn tự động đến một phần tử cụ thể ở giữa danh sách (gợi ý: element.scrollIntoView()).

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