В чем разница между useRef и createRef?

102

Я просматривал документацию по хукам, когда наткнулся на нее 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существует?

Рико Калер
источник

Ответы:

131

Разница в том, что 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возвращает одну и ту же ссылку при каждой визуализации экземпляра функционального компонента. Это то, что позволяет состоянию ссылки сохраняться между рендерами, несмотря на то, что вы явно нигде не сохраняете ее.

Во втором примере ссылка будет воссоздаваться при каждой визуализации.

Джо Клей
источник
Это неверно, у вас есть ссылка для подтверждения вашего заявления?
Адил Имран
Здесь есть комментарий одного из разработчиков React, объясняющий, как это работает: reddit.com/r/reactjs/comments/a2pt15/… Мне было бы интересно узнать, что вы думаете о неправильном ответе.
Джо Клей
Я видел эту ссылку, прежде чем попытался ответить на этот вопрос, где этот факт указывается в ссылке, которой вы поделились? Я не нашел? :)
Адил Имран
1
Ссылка, которой я поделился, показывает упрощенную реализацию useRef, опубликованную одним из разработчиков React. Это не то же самое, что простой вызов createRef, поскольку createRefэто не ловушка и не сохраняется какое-либо состояние между вызовами. Ответ Райана Когсвелла также является хорошим примером различий.
Джо Клей
1
Насколько я понимаю, из этого контекста можно сделать вывод, что useRef - это настраиваемый хук, который использует createRef внутри. Спасибо за то, что поделились знаниями.
Адил Имран
5

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, когда новое значение устанавливается с помощью метода установки. Если мы мутировать состояние useStateнепосредственно (не против сеттеров вызова), то его поведение более или менее становится эквивалентом к useRef, так как без повторного рендеринга срабатывают больше:

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

Примечание: не делайте этого! Используйте оптимизированный useRefAPI вместо того, чтобы изобретать колесо. Выше для иллюстрации.

ford04
источник
3

Просто чтобы выделить цель:

createRefтак же просто, как return {current: null}. Это способ обрабатывать ref=опору самым современным способом, и все (в то время как основанный на строках - это слишком магия, а на основе обратного вызова выглядит слишком многословно).

useRefсохраняет некоторые данные перед рендерингом, и их изменение не вызывает повторного рендеринга (как это useStateпроисходит). Они редко связаны. Все, что вы ожидаете от компонента на основе классов, переходит в поля экземпляра ( this.* =), похоже, кандидат для реализации useRefв функциональных компонентах.

Say useCallbackработает как методы ограниченного класса ( this.handleClick = .....bind(this)) и может быть повторно реализован (но мы точно не должны изобретать колесо) с помощью useRef.

Другими примерами являются ссылки на DOM, идентификаторы тайм-аута / интервала, идентификаторы или ссылки любых сторонних библиотек.

PS Я считаю, что команде React лучше выбрать другое название, useRefчтобы избежать путаницы createRef. Может быть useAndKeepили даже usePermanent.

Skyboyer
источник
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