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



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


createRef всегда возвращает новую ссылку, которую вы обычно сохраняете как поле в экземпляре компонента класса. useRef возвращает тот же реф. при каждом рендеринге экземпляра функционального компонента. Это то, что позволяет состоянию ссылки сохраняться между рендерингами, несмотря на то, что вы явно нигде его не сохраняете.
Во втором примере ссылка будет воссоздаваться при каждом рендеринге.
Здесь есть комментарий одного из разработчиков React, объясняющий, как это работает: reddit.com/r/reactjs/comments/a2pt15/… Мне было бы интересно узнать, что вы считаете неправильным в этом ответе.
Я видел эту ссылку до того, как попытался ответить на этот вопрос, где в ссылке, которой вы поделились, говорится об этом факте? Я не мог найти это? :)
Ссылка, которой я поделился, показывает упрощенную реализацию useRef, опубликованную одним из разработчиков React. Это не то же самое, что просто вызвать createRef, поскольку createRef не является ловушкой и не сохраняет никакого состояния между вызовами. В ответе Райана Когсвелла также есть хороший пример различий.
Насколько я понял из этого контекста, можно сделать вывод, что useRef — это настраиваемый хук, внутри которого используется createRef. Спасибо, что поделились знаниями.
Разница в том, что 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);
d= (^-^ ) хорошо, что ref не ограничен и может даже содержать простое число; Но зачем нужен .current (в отличие от хука useState)? Нашел причину: просто для того, чтобы сделать .current доступным по ссылке, как поле реального класса, без странного сеттера. (LOL, интересно, насколько медленнее функциональный класс по сравнению с реальным классом в наши дни.)
Просто чтобы выделить цель:
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.
ref — это простой JS-объект { current: <some value> }.
React.createRef() — это фабрика, возвращающая реф { current: null } — никакой магии.
useRef(initValue) также возвращает ссылку { current: initValue }, аналогичную React.createRef(). Кроме, запоминает эта ссылка должна быть постоянной для нескольких рендеров в функциональный компонент.
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.
useRef на useState + createRefСледующее твит было для меня поучительным:
useRef()is basicallyuseState({current: initialValue })[0].
Теперь, благодаря информации из раздела tldr, мы можем сделать следующий вывод:
useRef(null)is basicallyuseState(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.
Это неверно, у вас есть ссылка, подтверждающая ваше утверждение?