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

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

Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
204
0
82 853
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

createRef всегда возвращает новую ссылку, которую вы обычно сохраняете как поле в экземпляре компонента класса. useRef возвращает тот же реф. при каждом рендеринге экземпляра функционального компонента. Это то, что позволяет состоянию ссылки сохраняться между рендерингами, несмотря на то, что вы явно нигде его не сохраняете.

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

Это неверно, у вас есть ссылка, подтверждающая ваше утверждение?

Adeel Imran 10.02.2019 21:38

Здесь есть комментарий одного из разработчиков React, объясняющий, как это работает: reddit.com/r/reactjs/comments/a2pt15/… Мне было бы интересно узнать, что вы считаете неправильным в этом ответе.

Joe Clay 10.02.2019 21:40

Я видел эту ссылку до того, как попытался ответить на этот вопрос, где в ссылке, которой вы поделились, говорится об этом факте? Я не мог найти это? :)

Adeel Imran 10.02.2019 21:44

Ссылка, которой я поделился, показывает упрощенную реализацию useRef, опубликованную одним из разработчиков React. Это не то же самое, что просто вызвать createRef, поскольку createRef не является ловушкой и не сохраняет никакого состояния между вызовами. В ответе Райана Когсвелла также есть хороший пример различий.

Joe Clay 10.02.2019 21:48

Насколько я понял из этого контекста, можно сделать вывод, что useRef — это настраиваемый хук, внутри которого используется createRef. Спасибо, что поделились знаниями.

Adeel Imran 10.02.2019 21:52
Ответ принят как подходящий

Разница в том, что createRef всегда будет создавать новую ссылку. В компоненте на основе классов вы обычно помещаете ссылку в свойство экземпляра во время создания (например, 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);

Edit 1rvwnj71x3

d= (^-^ ) хорошо, что ref не ограничен и может даже содержать простое число; Но зачем нужен .current (в отличие от хука useState)? Нашел причину: просто для того, чтобы сделать .current доступным по ссылке, как поле реального класса, без странного сеттера. (LOL, интересно, насколько медленнее функциональный класс по сравнению с реальным классом в наши дни.)

Top-Master 16.10.2021 20:36

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

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

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

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

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

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

Еще одно, но важное дополнение к ответам других.

Вы не можете установить новое значение для 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 06.05.2020 17:05

tldr

ref — это простой JS-объект { current: <some value> }.

React.createRef() — это фабрика, возвращающая реф { current: null }никакой магии.

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

It is sufficient to use React.createRef in class components, as the ref object is присваивается переменной экземпляра, hence accessible throughout the component and its lifecyle:
this.myRef = React.createRef(); // stores ref in "mutable" this context (class)

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


1 Заменить useRef на useState + createRef

Следующее твит было для меня поучительным:

useRef() is basically useState({current: initialValue })[0].

Теперь, благодаря информации из раздела tldr, мы можем сделать следующий вывод:

useRef(null) is basically 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

Note: Don't do this! Use the optimized useRef API instead of reinventing the wheel. Above is for illustration purposes.

Другие вопросы по теме