Sự khác biệt giữa `useRef` và` createRef` là gì?


104

Tôi đã xem qua tài liệu hooks khi tôi tình cờ gặp useRef.

Nhìn vào ví dụ của họ…

function TextInputWithFocusButton() {
  const inputEl = useRef(null);
  const onButtonClick = () => {
    // `current` points to the mounted text input element
    inputEl.current.focus();
  };
  return (
    <>
      <input ref={inputEl} type="text" />
      <button onClick={onButtonClick}>Focus the input</button>
    </>
  );
}

… Nó có vẻ như useRefcó thể được thay thế bằng createRef.

function TextInputWithFocusButton() {
  const inputRef = createRef(); // what's the diff?
  const onButtonClick = () => {
    // `current` points to the mounted text input element
    inputRef.current.focus();
  };
  return (
    <>
      <input ref={inputRef} type="text" />
      <button onClick={onButtonClick}>Focus the input</button>
    </>
  );
}

Tại sao tôi cần một hook cho refs? Tại sao useReftồn tại?

Câu trả lời:


132

Sự khác biệt là createRefsẽ luôn tạo ra một ref mới. Trong một thành phần dựa trên lớp, bạn thường đặt tham chiếu vào một thuộc tính cá thể trong quá trình xây dựng (ví dụ this.input = createRef():). Bạn không có tùy chọn này trong một thành phần chức năng. useRefquan tâm đến việc trả lại cùng một ref mỗi lần như khi kết xuất ban đầu.

Dưới đây là một ứng dụng ví dụ thể hiện sự khác biệt trong hoạt động của hai chức năng này:

import React, { useRef, createRef, useState } from "react";
import ReactDOM from "react-dom";

function App() {
  const [renderIndex, setRenderIndex] = useState(1);
  const refFromUseRef = useRef();
  const refFromCreateRef = createRef();
  if (!refFromUseRef.current) {
    refFromUseRef.current = renderIndex;
  }
  if (!refFromCreateRef.current) {
    refFromCreateRef.current = renderIndex;
  }
  return (
    <div className="App">
      Current render index: {renderIndex}
      <br />
      First render index remembered within refFromUseRef.current:
      {refFromUseRef.current}
      <br />
      First render index unsuccessfully remembered within
      refFromCreateRef.current:
      {refFromCreateRef.current}
      <br />
      <button onClick={() => setRenderIndex(prev => prev + 1)}>
        Cause re-render
      </button>
    </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

Chỉnh sửa 1rvwnj71x3


34

createRefluôn trả về một tham chiếu mới, mà bạn thường lưu trữ dưới dạng một trường trên thể hiện của một thành phần lớp. useReftrả về cùng một tham chiếu trên mỗi lần hiển thị của một phiên bản của thành phần chức năng. Đây là điều cho phép trạng thái của tham chiếu tồn tại giữa các lần hiển thị, mặc dù bạn không lưu trữ nó ở bất kỳ đâu.

Trong ví dụ thứ hai của bạn, ref sẽ được tạo lại sau mỗi lần render.


Điều này không chính xác, bạn có tài liệu tham khảo để sao lưu báo cáo của mình không?
Adeel Imran

Có một nhận xét ở đây bởi một trong những nhà phát triển React giải thích rằng đây là cách nó hoạt động: reddit.com/r/reactjs/comments/a2pt15/… Tôi muốn biết bạn nghĩ gì không chính xác về câu trả lời này.
Joe Clay

Tôi đã nhìn thấy liên kết đó trước khi tôi thử trả lời câu hỏi này, nó thể hiện sự thật này ở đâu trong liên kết mà bạn đã chia sẻ? Tôi không thể tìm thấy nó? :)
Adeel Imran

1
Liên kết tôi đã chia sẻ cho thấy một triển khai đơn giản useRef, được đăng bởi một trong những nhà phát triển React. Nó không giống như đơn giản là gọi createRef, createRefkhông phải là hook và không tồn tại bất kỳ trạng thái nào giữa các cuộc gọi. Câu trả lời của Ryan Cogswell cũng có một ví dụ điển hình về sự khác biệt.
Joe Clay

1
Sự hiểu biết của tôi từ ngữ cảnh đó chỉ suy ra rằng useRef là một móc tùy chỉnh sử dụng createRef bên trong. Cảm ơn bạn đã chia sẻ kiến ​​thức.
Adeel Imran

6

tldr

A reflà một đối tượng JS thuần túy { current: <some value> }.

React.createRef()là một nhà máy trả lại một ref { current: null }- không liên quan đến phép thuật .

useRef(initValue)cũng trả về một tham chiếu { current: initValue }tương tự như React.createRef(). Bên cạnh đó , nó ghi nhớ tham chiếu này liên tục qua nhiều lần hiển thị trong một thành phần hàm .

Nó là đủ để sử dụng React.createReftrong các thành phần lớp, vì đối tượng ref được gán cho một biến thể hiện , do đó có thể truy cập được trong toàn bộ thành phần và kiểu sống của nó:
this.myRef = React.createRef(); // stores ref in "mutable" this context (class)

useRef(null)về cơ bản là tương đương với useState(React.createRef())[0] 1 .


1 Thay thế useRefbằng useState+createRef

Tweet sau đã khai sáng cho tôi:

useRef()về cơ bản là useState({current: initialValue })[0].

Với thông tin chi tiết từ tldrphần này, bây giờ chúng ta có thể kết luận thêm:

useRef(null)về cơ bản là useState(React.createRef())[0].

Đoạn mã trên "lạm dụng" useStateđể duy trì ref trả về từ React.createRef(). [0]chỉ cần chọn phần giá trị của useState- [1]sẽ là setter.

useStategây ra kết xuất lại trái ngược với useRef. Chính thức hơn, React so sánh tham chiếu đối tượng cũ và mới useStatekhi một giá trị mới được đặt thông qua phương thức setter của nó. Nếu chúng ta thay đổi trạng thái useStatetrực tiếp (trái ngược với lệnh gọi setter), hành vi của nó ít nhiều trở nên tương đương với useRef, vì không có hiển thị lại nào được kích hoạt nữa:

// Example of mutaing object contained in useState directly
const [ref] = useState({ current: null })
ref.current = 42; // doesn't cause re-render

Lưu ý: Đừng làm điều này! Sử dụng useRefAPI được tối ưu hóa thay vì phát minh lại bánh xe. Trên đây chỉ mang tính chất minh họa.


3

Chỉ để làm nổi bật một mục đích:

createReflà đơn giản như return {current: null}. Đó là một cách để xử lý ref=prop theo cách hiện đại nhất và đó là nó (trong khi dựa trên chuỗi là cách quá ma thuật và dựa trên gọi lại trông quá dài dòng).

useRefgiữ một số dữ liệu trước khi kết xuất và việc thay đổi nó không gây ra kết xuất lại (như useStatevậy). Chúng hiếm khi có liên quan. Mọi thứ bạn mong đợi đối với thành phần dựa trên lớp sẽ chuyển đến các trường thể hiện ( this.* =) trông giống như ứng viên sẽ được triển khai useReftrong các thành phần chức năng.

Say useCallbackhoạt động như các phương thức lớp bị giới hạn ( this.handleClick = .....bind(this)) và có thể được thực hiện lại (nhưng chắc chắn chúng ta không nên phát minh lại bánh xe) với useRef.

Một ví dụ khác là tham chiếu DOM, ID thời gian chờ / khoảng thời gian, bất kỳ số nhận dạng hoặc tham chiếu nào của thư viện bên thứ ba.

Tái bút Tôi tin rằng nhóm React nên chọn cách đặt tên khác useRefđể tránh nhầm lẫn với createRef. Có thể useAndKeephoặc thậm chí usePermanent.


1

Tuy nhiên, một bổ sung khác nhưng quan trọng cho các câu trả lời khác.

Bạn không thể đặt giá trị mới cho createRef. Nhưng bạn có thể cho useRef.

const ur = useRef();
const cr = createRef();

ur.current = 10; // you can do it, and value is set
cr.current = 10; // you can, but it's no good, it will not change it

ref là một đối tượng đơn giản, bạn có thể thay đổi thuộc tính của nó currentnhư bình thường (chỉ cần kiểm tra điều đó). Không thành vấn đề, nếu ref được tạo qua useRefhoặc createRef.
ford04
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.