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 useRef
se 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 refs? ¿Por qué existe useRef
?
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 el renderizado 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);
createRef
siempre devuelve una nueva referencia, que generalmente almacenaría 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 los renderizados, a pesar de que no lo almacene explícitamente en ningún lugar.
En su segundo ejemplo, la referencia se volvería a crear en cada renderizado.
Una ref
es un objeto JS simple { current: <some value> }
.
React.createRef()
es una fábrica que devuelve una referencia { current: null }
, sin magia involucrada .
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 .
React.createRef
en componentes de clase, ya que el objeto ref se asigna a una variable de instancia y, 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 .
useRef
con useState
+ createRef
El siguiente tweet ha sido esclarecedor para mí:
useRef()
es básicamenteuseState({current: initialValue })[0]
.
Con los conocimientos de la sección tldr
, ahora podemos concluir aún más:
useRef(null)
es básicamenteuseState(React.createRef())[0]
.
El código anterior "abusa" de useState
para conservar la referencia devuelta de React.createRef()
. [0]
simplemente selecciona la parte de valor de useState
- [1]
sería el setter.
useState
provoca una nueva representación en contraste con useRef
. Más formalmente, React compara la referencia de objeto antiguo y nuevo 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 oposición a la invocación del setter), su comportamiento se vuelve más o menos equivalente a useRef
, ya que ya no se activa una nueva representación:
// Example of mutaing object contained in useState directly const [ref] = useState({ current: null }) ref.current = 42; // doesn't cause re-render
Nota: ¡No hagas esto! Utilice la API useRef
optimizada en lugar de reinventar la rueda. Lo anterior es para fines ilustrativos.