¿Cuál es la diferencia entre `useRef` y` createRef`?

102

Estaba revisando la documentación de los ganchos cuando me encontré 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 useRefse 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 árbitros? ¿Por qué useRefexiste?

Rico Kahler
fuente

Respuestas:

130

La diferencia es que createRefsiempre creará una nueva ref. En un componente basado en clases, normalmente pondrí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. useRefse 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);

Editar 1rvwnj71x3

Ryan Cogswell
fuente
34

createRefsiempre devuelve una nueva referencia, que generalmente almacenarías como un campo en la instancia de un componente de clase. useRefdevuelve la misma referencia en cada renderizado de la instancia de un componente funcional. Esto es lo que permite que el estado de la referencia persista entre 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 render.

Joe Clay
fuente
Esto es incorrecto, ¿tiene una referencia para respaldar su declaración?
Adeel Imran
Aquí hay un comentario de uno de los desarrolladores de React que explica que así es como funciona: reddit.com/r/reactjs/comments/a2pt15/ ... Me interesaría saber qué crees que es incorrecto sobre esta respuesta.
Joe Clay
Vi ese enlace antes de intentar responder a esta pregunta, ¿dónde dice este hecho en el enlace que ha compartido? ¿No pude encontrarlo? :)
Adeel Imran
1
El enlace que compartí muestra una implementación simplificada de useRef, publicado por uno de los desarrolladores de React. No es lo mismo que simplemente llamar createRef, ya createRefque no es un gancho y no persiste ningún estado entre llamadas. La respuesta de Ryan Cogswell también tiene un buen ejemplo de las diferencias.
Joe Clay
1
Mi entendimiento de ese contexto solo infirió que useRef es un gancho personalizado que usa createRef adentro. Gracias por compartir el conocimiento.
Adeel Imran
5

tldr

A refes un objeto JS simple { current: <some value> }.

React.createRef()es una fábrica que devuelve un árbitro { 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 representaciones en un componente de función .

Es suficiente usar React.createRefen componentes de clase, ya que el objeto ref se asigna a una variable de instancia , por lo tanto, accesible en 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 useRefcon useState+createRef

El siguiente tweet ha sido esclarecedor para mí:

useRef()es básicamente useState({current: initialValue })[0].

Con los conocimientos de la tldrsección, ahora podemos concluir más:

useRef(null)es básicamente useState(React.createRef())[0].

El código anterior "abusa" useStatepara conservar la referencia devuelta React.createRef(). [0]simplemente selecciona la parte del valor de useState- [1]sería el definidor.

useStateprovoca una repetición en contraste con useRef. Más formalmente, React compara la referencia del objeto nuevo y antiguo para useState, cuando se establece un nuevo valor a través de su método de establecimiento. Si mutamos el estado de useStatedirectamente (opuesto a la invocación del establecedor), su comportamiento se vuelve más o menos equivalente a useRef, ya que ya no se activa ninguna repetició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 useRefAPI optimizada en lugar de reinventar la rueda. Lo anterior es para fines ilustrativos.

ford04
fuente
3

Solo para resaltar un propósito:

createRefes tan simple como return {current: null}. Es una forma de manejar ref=prop de la manera más moderna y eso es todo (mientras que la función de cadena es demasiado mágica y la devolución de llamada parece demasiado detallada).

useRefmantiene algunos datos antes del renderizado y cambiarlos no provoca que se vuelva a renderizar (como lo useStatehace). Rara vez están relacionados. Todo lo que espera para el componente basado en clases va a los campos de instancia ( this.* =) parece candidato para ser implementado useRefen componentes funcionales.

Say useCallbackfunciona como métodos de clase acotada ( this.handleClick = .....bind(this)) y puede volver a implementarse (pero no deberíamos reinventar la rueda con seguridad) useRef.

Otros ejemplos son las referencias DOM, los ID de tiempo de espera / intervalo, los identificadores o referencias de cualquier biblioteca de terceros.

PD: Creo que el equipo de React eligió un nombre diferente para useRefevitar confusiones createRef. Quizás useAndKeepo incluso usePermanent.

Skyboyer
fuente
1

Sin embargo, otra adición importante a las respuestas de otros.

No puede establecer un nuevo valor para createRef. Pero puedes por 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
Verde
fuente
ref es un objeto simple, puede cambiar su currentpropiedad como de costumbre (solo lo probé). No importa si la referencia se crea mediante useRefo createRef.
Ford04