Saltar al contenido

Cuándo usar useImperativeHandle, useLayoutEffect y useDebugValue

Solución:

Permítame comenzar esta respuesta indicando que todos estos ganchos se usan muy raramente. El 99% del tiempo, no los necesitará. Solo están destinados a cubrir algunos escenarios raros de casos de esquina.


useImperativeHandle

Por lo general, cuando usa useRef se le da el valor de instancia del componente ref se adjunta a. Esto le permite interactuar con el elemento DOM directamente.

useImperativeHandle es muy similar, pero te permite hacer dos cosas:

  1. Le da control sobre el valor que se devuelve. En lugar de devolver el elemento de la instancia, indica explícitamente cuál será el valor de retorno (consulte el fragmento a continuación).
  2. Le permite reemplazar funciones nativas (como blur, focus, etc.) con funciones propias, permitiendo así efectos secundarios al comportamiento normal, o un comportamiento completamente diferente. Sin embargo, puedes llamar a la función como quieras.

Puede haber muchas razones por las que desee hacer cualquiera de las anteriores; es posible que no desee exponer propiedades nativas al padre o tal vez desee cambiar el comportamiento de una función nativa. Podría haber muchas razones. Sin embargo, useImperativeHandle rara vez se utiliza.

useImperativeHandle personaliza el valor de la instancia que se expone a los componentes principales cuando se utiliza ref

Ejemplo

En este ejemplo, el valor que obtendremos del ref solo contendrá la función blur que declaramos en nuestro useImperativeHandle. No contendrá ninguna otra propiedad (Estoy registrando el valor para demostrar esto). La función en sí también está “personalizada” para comportarse de manera diferente a lo que normalmente esperarías. Aquí, se pone document.title y difumina la entrada cuando blur se invoca.

const MyInput = React.forwardRef((props, ref) => {
  const [val, setVal] = React.useState('');
  const inputRef = React.useRef();

  React.useImperativeHandle(ref, () => ({
    blur: () => {
      document.title = val;
      inputRef.current.blur();
    }
  }));

  return (
    <input
      ref={inputRef}
      val={val}
      onChange={e => setVal(e.target.value)}
      {...props}
    />
  );
});

const App = () => {
  const ref = React.useRef(null);
  const onBlur = () => {
    console.log(ref.current); // Only contains one property!
    ref.current.blur();
  };

  return <MyInput ref={ref} onBlur={onBlur} />;
};

ReactDOM.render(<App />, document.getElementById("app"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.1/umd/react-dom.production.min.js"></script>
<div id="app"></div>


useLayoutEffect

Si bien es similar en cierta medida a useEffect(), difiere en que se ejecutará después de que React haya comprometido actualizaciones al DOM. Se usa en casos excepcionales cuando necesita calcular la distancia entre elementos después de una actualización o hacer otros cálculos / efectos secundarios posteriores a la actualización.

La firma es idéntica a useEffect, pero se dispara sincrónicamente después de todas las mutaciones DOM. Use esto para leer el diseño del DOM y volver a renderizar sincrónicamente. Actualizaciones programadas en el interior useLayoutEffect se lavará sincrónicamente, antes de que el navegador tenga la oportunidad de pintar.

Ejemplo

Suponga que tiene un elemento absolutamente posicionado cuya altura puede variar y desea colocar otro div debajo de ella. Podrías usar getBoundingCLientRect() para calcular la altura del padre y las propiedades superiores y luego aplicarlas a la propiedad superior del hijo.

Aquí querrías usar useLayoutEffect en vez de useEffect. Vea por qué en los ejemplos siguientes:

Con useEffect: (observe el comportamiento nervioso)

const Message = ({boxRef, children}) => {
  const msgRef = React.useRef(null);
  React.useEffect(() => {
    const rect = boxRef.current.getBoundingClientRect();
    msgRef.current.style.top = `${rect.height + rect.top}px`;
  }, []);

  return <span ref={msgRef} className="msg">{children}</span>;
};

const App = () => {
  const [show, setShow] = React.useState(false);
  const boxRef = React.useRef(null);

  return (
    <div>
      <div ref={boxRef} className="box" onClick={() => setShow(prev => !prev)}>Click me</div>
      {show && <Message boxRef={boxRef}>Foo bar baz</Message>}
    </div>
  );
};

ReactDOM.render(<App />, document.getElementById("app"));
.box {
  position: absolute;
  width: 100px;
  height: 100px;
  background: green;
  color: white;
}

.msg {
  position: relative;
  border: 1px solid red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.1/umd/react-dom.production.min.js"></script>
<div id="app"></div>

Con useLayoutEffect:

const Message = ({boxRef, children}) => {
  const msgRef = React.useRef(null);
  React.useLayoutEffect(() => {
    const rect = boxRef.current.getBoundingClientRect();
    msgRef.current.style.top = `${rect.height + rect.top}px`;
  }, []);

  return <span ref={msgRef} className="msg">{children}</span>;
};

const App = () => {
  const [show, setShow] = React.useState(false);
  const boxRef = React.useRef(null);

  return (
    <div>
      <div ref={boxRef} className="box" onClick={() => setShow(prev => !prev)}>Click me</div>
      {show && <Message boxRef={boxRef}>Foo bar baz</Message>}
    </div>
  );
};

ReactDOM.render(<App />, document.getElementById("app"));
.box {
  position: absolute;
  width: 100px;
  height: 100px;
  background: green;
  color: white;
}

.msg {
  position: relative;
  border: 1px solid red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.1/umd/react-dom.production.min.js"></script>
<div id="app"></div>


useDebugValue

A veces, es posible que desee depurar ciertos valores o propiedades, pero hacerlo puede requerir operaciones costosas que pueden afectar el rendimiento.

useDebugValue solo se llama cuando las React DevTools están abiertas y se inspecciona el enlace relacionado, lo que evita cualquier impacto en el rendimiento.

useDebugValue se puede usar para mostrar una etiqueta para ganchos personalizados en React DevTools.

Sin embargo, personalmente nunca he usado este gancho. Tal vez alguien en los comentarios pueda dar una idea con un buen ejemplo.

useImperativeHandle

useImperativeHandle le permite determinar qué propiedades se expondrán en una ref. En el siguiente ejemplo, tenemos un componente de botón y nos gustaría exponer el someExposedProperty propiedad en esa ref:

[index.tsx]

import React, { useRef } from "react";
import { render } from "react-dom";
import Button from "./Button";

import "./styles.css";

function App() {
  const buttonRef = useRef(null);

  const handleClick = () => {
    console.log(Object.keys(buttonRef.current)); // ['someExposedProperty']
    console.log("click in index.tsx");
    buttonRef.current.someExposedProperty();
  };

  return (
    <div>
      <Button onClick={handleClick} ref={buttonRef} />
    </div>
  );
}

const rootElement = document.getElementById("root");
render(<App />, rootElement);

[Button.tsx]

import React, { useRef, useImperativeHandle, forwardRef } from "react";

function Button(props, ref) {
  const buttonRef = useRef();
  useImperativeHandle(ref, () => ({
    someExposedProperty: () => {
      console.log(`we're inside the exposed property function!`);
    }
  }));
  return (
    <button ref={buttonRef} {...props}>
      Button
    </button>
  );
}

export default forwardRef(Button);

Disponible aquí.

useLayoutEffect

Esto es lo mismo que useEffect, pero solo se activa una vez que se completan todas las mutaciones DOM. Este artículo de Kent C. Dodds explica la diferencia tan bien como cualquiera, con respecto a estos dos, dice:

99% del tiempo [useEffect] es lo que quieres usar.

No he visto ningún ejemplo que ilustre esto particularmente bien, y tampoco estoy seguro de que pueda crear nada. Probablemente sea mejor decir que solo debes usar useLayoutEffect cuando useEffect tiene problemas.

useDebugValue

Siento que los documentos son un buen ejemplo de cómo explicar este. Si tiene un gancho personalizado y le gustaría etiquetarlo dentro de React DevTools, entonces esto es lo que usa.

Si tiene algún problema específico con esto, probablemente sea mejor comentar o hacer otra pregunta, porque siento que cualquier cosa que la gente ponga aquí será simplemente reiterando los documentos, al menos hasta que lleguemos a un problema más específico.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)


Tags : /

Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *