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

Resuelto Rico Kahler asked hace 5 años • 6 respuestas

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 useRefpuede 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é useRefexiste?

Rico Kahler avatar Feb 11 '19 03:02 Rico Kahler
Aceptado

La diferencia es que createRefsiempre 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. useRefse 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);

Editar 1rvwnj71x3

Ryan Cogswell avatar Feb 10 '2019 20:02 Ryan Cogswell

tldr

A refes 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.createRefen 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 useRefcon useState+createRef

El siguiente tweet ha sido esclarecedor para mí:

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

Con las ideas de la tldrsección, ahora podemos concluir aún más:

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

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

useStateprovoca 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 useStatedirectamente (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 useRefAPI optimizada en lugar de reinventar la rueda. Lo anterior tiene fines ilustrativos.

ford04 avatar May 06 '2020 15:05 ford04

createRefsiempre devuelve una nueva referencia, que generalmente se almacena como un campo en la instancia de un componente de clase. useRefdevuelve 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.

Joe Clay avatar Feb 10 '2019 20:02 Joe Clay

Sólo para resaltar un propósito:

createRefes 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).

useRefmantiene algunos datos antes de renderizarlos y cambiarlos no provoca que se vuelvan a renderizar (como useStateocurre). 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 useRefen componentes funcionales.

Say useCallbackfunciona 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 useRefevitar confusiones createRef. Quizás useAndKeepo incluso usePermanent.

skyboyer avatar Sep 16 '2019 06:09 skyboyer