reactjs

reactjs - Cuándo usar useImperativeHandle, useLayoutEffect y useDebugValue



(2)

No puedo entender por qué se necesitan los siguientes ganchos UseImperativeHandle, useLayoutEffect y useDebugValue, ¿puede dar ejemplos de cuándo se pueden usar, pero no ejemplos de la documentación, por favor?


useImperativeHandle

Imagina que creas una Table componentes. Es posible que desee especificar cómo las refs en la Table interactúan con ese componente; puede ser que, por ejemplo, desee proporcionar una celda en particular, en lugar de poder acceder al elemento raíz.

[index.tsx]

import React, { useRef, useEffect } from "react"; import { render } from "react-dom"; import Table from "./Table"; import "./styles.css"; function App() { const tableRef = useRef(null); useEffect(() => { tableRef.current.style.color = "green"; }, []); return ( <div> <Table ref={tableRef} /> </div> ); } const rootElement = document.getElementById("root"); render(<App />, rootElement);

[Table.tsx]

import React, { useRef, useImperativeHandle, forwardRef } from "react"; function Table(props, ref) { const cellRef = useRef(null); useImperativeHandle(ref, () => cellRef.current); return ( <table> <tr> <td>First</td> <td ref={cellRef}>Second</td> </tr> </table> ); } export default forwardRef(Table);

Disponible aquí.

useLayoutEffect

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

El 99% del tiempo [ useEffect ] es lo que desea 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 debe usar useLayoutEffect cuando useEffect tiene problemas.

useDebugValue

Siento que la documentación es un buen ejemplo de cómo explicar esto. Si tiene un gancho personalizado y desea etiquetarlo dentro de React DevTools, entonces esto es lo que usa.

Si tiene algún problema específico con esto, entonces probablemente sería mejor hacer un comentario o hacer otra pregunta, porque creo que cualquier cosa que pongan aquí simplemente reiterará los documentos, al menos hasta que lleguemos a un problema más específico.


Permítanme prefaciar esta respuesta afirmando que todos estos ganchos se usan muy raramente. El 99% de las veces, no las necesitarás. Sólo están destinados a cubrir algunos escenarios de esquinas poco comunes.

useImperativeHandle

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

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

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

Puede haber muchas razones por las que quiera hacer alguna de las dos opciones anteriores; es posible que no desee exponer las 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 instancia que se expone a los componentes primarios cuando se usa ref

Ejemplo

En este ejemplo, el valor que obtendremos de la ref solo contendrá la función de blur que useImperativeHandle 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 se espera. Aquí, establece document.title y difumina la entrada cuando se invoca blur .

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

Aunque es similar en cierta medida a useEffect() , difiere en que se ejecutará después de que React haya confirmado las actualizaciones del DOM. Se utiliza en casos excepcionales cuando necesita calcular la distancia entre elementos después de una actualización o realizar otros cálculos / efectos secundarios posteriores a la actualización.

La firma es idéntica a useEffect , pero se dispara de forma síncrona después de todas las mutaciones de DOM. Use esto para leer el diseño del DOM y volver a renderizar de forma sincrónica. Las actualizaciones programadas dentro de useLayoutEffect se useLayoutEffect sincrónicamente, antes de que el navegador tenga la oportunidad de pintar .

Ejemplo

Supongamos que tiene un elemento absolutamente posicionado cuya altura puede variar y desea colocar otro div debajo de él. Puede usar getBoundingCLientRect() para calcular la altura y las propiedades principales del padre y luego aplicarlas a la propiedad superior del hijo.

Aquí le gustaría usar useLayoutEffect lugar de useEffect . Vea por qué en los ejemplos a continuación:

Con useEffect : (note 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 podrían afectar el rendimiento.

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

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