У чому різниця між `useRef` і` createRef`?


104

Я переглядав документацію про гачки, коли натрапив useRef.

Дивлячись на їх приклад ...

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>
    </>
  );
}

... здається, це useRefможна замінити на 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>
    </>
  );
}

Навіщо мені гачок для реф? Чому useRefіснує?

Відповіді:


132

Різниця полягає в тому createRef, що завжди буде створюватися новий реф. У компоненті на основі класу ви зазвичай вкладаєте ref у властивість екземпляра під час побудови (наприклад this.input = createRef()). У вас немає цієї опції у компоненті функції. useRefдбає про повернення того самого посилання кожного разу, що і при початковому візуалізації.

Ось приклад програми, що демонструє різницю в поведінці цих двох функцій:

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);

Редагувати 1rvwnj71x3


34

createRefзавжди повертає нове посилання, яке ви зазвичай зберігаєте як поле на екземплярі компонента класу. useRefповертає однакове посилання при кожному рендерінгу екземпляра функціонального компонента. Це те, що дозволяє зберегти стан посилання між рендерами, незважаючи на те, що ви ніде не зберігаєте його явно.

У вашому другому прикладі посилання буде відтворюватися при кожному рендері.


Це неправильно. Чи є у вас посилання для резервного копіювання виписки?
Adeel Imran

Тут є коментар одного з розробників React, який пояснює, що так це працює: reddit.com/r/reactjs/comments/a2pt15/... Мені було б цікаво дізнатись, що ви вважаєте неправильним щодо цієї відповіді.
Джо Клей

Я бачив це посилання ще до того, як спробував відповісти на це запитання, де в ньому вказано цей факт у посиланні, яким ви поділилися? Я не міг його знайти? :)
Адель Імран

1
Посилання, яким я поділився, показує спрощену реалізацію useRef, опубліковану одним із розробників React. Це не те саме, що просто дзвонити createRef, оскільки createRefце не гачок і не зберігається будь-який стан між дзвінками. Відповідь Райана Когсуела також має хороший приклад відмінностей.
Joe Clay

1
Я зрозумів з цього контексту лише висновок, що useRef - це спеціальний хук, який використовує createRef всередині. Дякуємо за обмін знаннями.
Adeel Imran

6

tldr

A ref- це звичайний JS-об'єкт { current: <some value> }.

React.createRef()є фабрика, що повертає реф { current: null }- ніякої магії не бере участь .

useRef(initValue)також повертає посилання, { current: initValue }подібне до React.createRef(). Крім того , він запам'ятовує це посилання як стійке в декількох візуалізаціях у компоненті функції .

Досить використовувати React.createRefв компонентах класу, оскільки об’єкт ref присвоюється змінній екземпляра , отже, доступній по всьому компоненту та його життєвому циклу:
this.myRef = React.createRef(); // stores ref in "mutable" this context (class)

useRef(null)в основному еквівалентно useState(React.createRef())[0] 1 .


1 Замініть useRefна useState+createRef

Наступний твіт для мене просвітив:

useRef()в основному useState({current: initialValue })[0].

На основі аналізів з tldrрозділу, тепер ми можемо зробити наступний висновок:

useRef(null)в основному useState(React.createRef())[0].

Наведений вище код "зловживає", useStateщоб зберегти повернене посилання з React.createRef(). [0]просто вибирає значення частини useState- [1]буде сеттером.

useStateвикликає рендеринг на відміну від useRef. Більш формально, React порівнює старе та нове посилання на об'єкт для useState, коли нове значення встановлюється за допомогою його методу setter. Якщо ми мутувати стан useStateбезпосередньо (Не проти сеттерів виклику), то його поведінка більш-менш стає еквівалентом до useRef, так як без повторного рендеринга спрацьовують більше:

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

Примітка: Не робіть цього! useRefЗамість оптимізованого колеса використовуйте оптимізований API. Вище для ілюстрації.


3

Тільки для виділення мети:

createRefтак само просто, як return {current: null}. Це спосіб обробляти ref=проп найсучаснішим способом, і все. (В той час як на основі рядків це занадто магія, а зворотний виклик виглядає занадто багатослівно).

useRefзберігає деякі дані перед візуалізацією, а їх зміна не викликає повторного візуалізації (як useStateце робиться). Вони рідко пов’язані між собою. Все, що ви очікуєте від компонента на основі класу, переходить до полів екземпляра ( this.* =), виглядає як кандидат, який має бути реалізований useRefу функціональних компонентах.

Скажімо, useCallbackпрацює як обмежений метод класу ( this.handleClick = .....bind(this)) і може бути повторно реалізований (але ми не повинні перевизначати колесо напевно) за допомогою useRef.

Іншими прикладами є посилання на DOM, ідентифікатори часу очікування / інтервалу, ідентифікатори або посилання будь-яких сторонніх бібліотек.

PS Я вважаю, що команді React краще вибрати інше іменування, useRefщоб уникнути плутанини createRef. Можливо useAndKeepчи навіть usePermanent.


1

Ще одне, але важливе доповнення до відповідей інших.

Ви не можете встановити нове значення для createRef. Але ви можете за 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 - це звичайний об'єкт, ви можете змінити його currentвластивість, як зазвичай (щойно перевірили). Не має значення, якщо посилання створено через useRefабо createRef.
ford04
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.