useImperativeHandle

useImperativeHandle es un Hook de React que te permite personalizar el identificador expuesto como un ref.

useImperativeHandle(ref, createHandle, dependencies?)

Referencia

useImperativeHandle(ref, createHandle, dependencies?)

Llama a useImperativeHandle en el nivel superior de tu componente para personalizar el identificador ref que se expone:

import { forwardRef, useImperativeHandle } from 'react';

const MyInput = forwardRef(function MyInput(props, ref) {
useImperativeHandle(ref, () => {
return {
// ... tus métodos ...
};
}, []);
// ...

Mira más ejemplos debajo.

Parámetros

  • ref: El ref que recibes como segundo argumento de la función render forwardRef

  • createHandle: Una función que no toma argumentos y devuelve el identificador ref que quieres exponer. El identificador ref que devuelve puede tener cualquier tipo. Por lo general, devolverá un objeto con lo métodos que quieres exponer.

  • opcional dependencies: La lista de todos los valores reactivos a los que se hace referencia dentro del código de createHandle. Los valores reactivos incluye props, estados, y todas las variables y funciones declaradas directamente dentro del cuerpo de tu componente. Si tu linter es configurado por React, va a verificar que cada valor reactivo esté correctamente especificado como una dependencia. La lista de dependencias deben tener un número constante de elementos y ser escritos en una sola linea como [dep1, dep2, dep3]. React comparará cada dependencia con su valor anterior usando el algoritmo de comparación Object.is. Si un nuevo renderizado resultara en un cambio a una dependencia, o si no especificaste las dependencias completamente, tu función createHandle se volverá a ejecutar, y el nuevo identificador recien creado será asignado a ref.

Devuelve

useImperativeHandle devuelve undefined.


Uso

Exponer un identificador ref personalizado al componente padre

Los componentes por defecto no exponen sus nodos DOM a los componentes padre. Por ejemplo, si quieres el componente padre de MyInput para tener acceso al nodo DOM de <input>, tienes que optar por forwardRef:

import { forwardRef } from 'react';

const MyInput = forwardRef(function MyInput(props, ref) {
return <input {...props} ref={ref} />;
});

Con el código de arriba, un ref a MyInput va a recibir el nodo DOM de <input>. Aun así, puedes exponer un valor personalizado en su lugar. Para personalizar el identificador expuesto, llama a useImperativeHandle en el nivel superior de tu componente:

import { forwardRef, useImperativeHandle } from 'react';

const MyInput = forwardRef(function MyInput(props, ref) {
useImperativeHandle(ref, () => {
return {
// ... tus métodos ...
};
}, []);

return <input {...props} />;
});

Ten en cuenta que en el código de arriba, el ref ya no se reenvía a <input>.

Por ejemplo, supongamos que no quieres exponer el nodo DOM entero de <input>, pero quieres exponer dos de sus métodos: focus y scrollIntoView. Para hacer esto, mantén el DOM real del navegador en un ref separado. Entonces usa useImperativeHandle para exponer un identificador solamente con los métodos que quieres que el componente padre llame:

import { forwardRef, useRef, useImperativeHandle } from 'react';

const MyInput = forwardRef(function MyInput(props, ref) {
const inputRef = useRef(null);

useImperativeHandle(ref, () => {
return {
focus() {
inputRef.current.focus();
},
scrollIntoView() {
inputRef.current.scrollIntoView();
},
};
}, []);

return <input {...props} ref={inputRef} />;
});

Ahora, si el componente padre obtiene un ref a MyInput, podrá llamar a los métodos focus y scrollIntoView en él. Sin embargo, no va a tener acceso completo al nodo DOM de <input> de manera mas profunda.

import { useRef } from 'react';
import MyInput from './MyInput.js';

export default function Form() {
  const ref = useRef(null);

  function handleClick() {
    ref.current.focus();
    //Esto no funcionará porque el nodo DOM no está expuesto
    // ref.current.style.opacity = 0.5;
  }

  return (
    <form>
      <MyInput label="Enter your name:" ref={ref} />
      <button type="button" onClick={handleClick}>
        Editar
      </button>
    </form>
  );
}


Exponer tus propios métodos imperativos

Los métodos que expones via identificador imperativo no tienen que coincidir exactamente a los métodos del DOM. Por ejemplo, el componente Post en el ejemplo de abajo expone a scrollAndFocusAddComment por medio de un identificador imperativo. Esto le permite a la Página padre desplazar la lista de comentarios y enfocar el campo de entrada cuando haces click al botón.

import { useRef } from 'react';
import Post from './Post.js';

export default function Page() {
  const postRef = useRef(null);

  function handleClick() {
    postRef.current.scrollAndFocusAddComment();
  }

  return (
    <>
      <button onClick={handleClick}>
        Escribe un comentario
      </button>
      <Post ref={postRef} />
    </>
  );
}

Atención

No sobreutilizar los refs. Solo debes usar los refs para comportamientos imperativos que no puedes expresar como props: por ejemplo desplazarse a un nodo, enfocar un nodo, activar una animación, seleccionar texto, etc.

Si puedes expresar algo como un prop, no deberias usar un ref. Por ejemplo, en vez de exponer un identificador imperativo como { open, close } del componente Modal, es mejor tomar isOpen como un prop, algo como <Modal isOpen={isOpen} />. Efectos puede ayudarte a exponer comportamientos imperativos via props.