¿Cuál es la diferencia entre "useRef" y "createRef"?
Estaba revisando la documentación de los ganchos cuando me topé con useRef
.
Mirando su ejemplo...
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>
</>
);
}
…parece que se useRef
puede reemplazar con 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>
</>
);
}
¿Por qué necesito un gancho para los árbitros? ¿Por qué useRef
existe?
La diferencia es que createRef
siempre creará una nueva referencia. En un componente basado en clases, normalmente colocaría la referencia en una propiedad de instancia durante la construcción (por ejemplo this.input = createRef()
). No tiene esta opción en un componente de función. useRef
se encarga de devolver la misma referencia cada vez que en la representación inicial.
Aquí hay una aplicación de ejemplo que demuestra la diferencia en el comportamiento de estas dos funciones:
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);
tldr
A ref
es un objeto JS simple { current: <some value> }
.
React.createRef()
Es una fábrica que devuelve un ref { current: null }
, sin magia .
useRef(initValue)
también devuelve una referencia { current: initValue }
similar a React.createRef()
. Además , memoriza esta referencia para que sea persistente en múltiples renderizaciones en un componente de función .
Es suficiente usarlo React.createRef
en componentes de clase, ya que el objeto ref se asigna a una variable de instancia , por lo tanto, es accesible durante todo el componente y su ciclo de vida:
this.myRef = React.createRef(); // stores ref in "mutable" this context (class)
useRef(null)
básicamente es equivalente a useState(React.createRef())[0]
1 .
1 Reemplazar useRef
con useState
+createRef
El siguiente tweet ha sido esclarecedor para mí:
useRef()
es básicamenteuseState({current: initialValue })[0]
.
Con las ideas de la tldr
sección, ahora podemos concluir aún más:
useRef(null)
es básicamenteuseState(React.createRef())[0]
.
El código anterior "abusa" useState
para conservar la referencia devuelta por React.createRef()
. [0]
simplemente selecciona la parte del valor de useState
- [1]
sería el definidor.
useState
provoca una nueva representación en contraste con useRef
. Más formalmente, React compara la referencia de objeto antigua y nueva para useState
, cuando se establece un nuevo valor a través de su método de establecimiento. Si mutamos el estado de useState
directamente (en contraposición a la invocación del setter), su comportamiento se vuelve más o menos equivalente a useRef
, ya que ya no se activa ninguna repetición de renderizado:
// Example of mutating object contained in useState directly
const [ref] = useState({ current: null })
ref.current = 42; // doesn't cause re-render
Nota: ¡No hagas esto! Utilice la useRef
API optimizada en lugar de reinventar la rueda. Lo anterior tiene fines ilustrativos.
createRef
siempre devuelve una nueva referencia, que generalmente se almacena como un campo en la instancia de un componente de clase. useRef
devuelve la misma referencia en cada representación de la instancia de un componente funcional. Esto es lo que permite que el estado de la referencia persista entre renderizaciones, a pesar de que no la almacene explícitamente en ningún lugar.
En su segundo ejemplo, la referencia se recrearía en cada renderizado.
Sólo para resaltar un propósito:
createRef
es tan simple como return {current: null}
. Es una forma de manejar ref=
accesorios de la manera más moderna y eso es todo (mientras que el basado en cadenas es demasiado mágico y el basado en devolución de llamadas parece demasiado detallado).
useRef
mantiene algunos datos antes de renderizarlos y cambiarlos no provoca que se vuelvan a renderizar (como useState
ocurre). Rara vez están relacionados. Todo lo que espera de un componente basado en clases, vaya a los campos de instancia ( this.* =
) parece candidato a implementarse useRef
en componentes funcionales.
Say useCallback
funciona como métodos de clase acotados ( this.handleClick = .....bind(this)
) y puede volver a implementarse (pero no deberíamos reinventar la rueda con seguridad) con useRef
.
Otros ejemplos son referencias DOM, ID de tiempo de espera/intervalo, identificadores o referencias de bibliotecas de terceros.
PD: Creo que es mejor que el equipo de React elija nombres diferentes para useRef
evitar confusiones createRef
. Quizás useAndKeep
o incluso usePermanent
.