useImperativeHandle

useImperativeHandle 是一个 React Hook,允许您自定义作为 ref 公开的句柄。

useImperativeHandle(ref, createHandle, dependencies?)

参考

useImperativeHandle(ref, createHandle, dependencies?)

在组件的顶层调用 useImperativeHandle 以自定义其公开的 ref 句柄

import { forwardRef, useImperativeHandle } from 'react';

const MyInput = forwardRef(function MyInput(props, ref) {
useImperativeHandle(ref, () => {
return {
// ... your methods ...
};
}, []);
// ...

请参阅下面的更多示例。

参数

  • ref:您从 forwardRef 渲染函数 中作为第二个参数接收的 ref

  • createHandle:一个不带参数的函数,返回您要公开的 ref 句柄。该 ref 句柄可以是任何类型。通常,您将返回一个包含要公开的方法的对象。

  • 可选 dependenciescreateHandle 代码中引用的所有响应式值的列表。响应式值包括 props、state 以及直接在组件体中声明的所有变量和函数。如果您的 linter 已为 React 配置,它将验证每个响应式值是否已正确指定为依赖项。依赖项列表必须具有恒定的项目数,并像 [dep1, dep2, dep3] 一样内联编写。React 将使用 Object.is 比较将每个依赖项与其先前值进行比较。如果重新渲染导致某些依赖项发生更改,或者您省略了此参数,则您的 createHandle 函数将重新执行,并且新创建的句柄将分配给 ref。

返回值

useImperativeHandle 返回 undefined


用法

将自定义 ref 句柄暴露给父组件

默认情况下,组件不会将其 DOM 节点暴露给父组件。例如,如果您希望 MyInput 的父组件可以访问 <input> DOM 节点,则必须使用 forwardRef 选择加入:

import { forwardRef } from 'react';

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

使用上面的代码,MyInput 的 ref 将接收 <input> DOM 节点。 但是,您可以改为公开自定义值。要自定义公开的句柄,请在组件的顶层调用 useImperativeHandle

import { forwardRef, useImperativeHandle } from 'react';

const MyInput = forwardRef(function MyInput(props, ref) {
useImperativeHandle(ref, () => {
return {
// ... your methods ...
};
}, []);

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

请注意,在上面的代码中,ref 不再转发到 <input>

例如,假设您不想公开整个 <input> DOM 节点,但您想公开它的两个方法:focusscrollIntoView。为此,请将真实的浏览器 DOM 保留在单独的 ref 中。然后使用 useImperativeHandle 公开一个句柄,该句柄仅包含您希望父组件调用的方法

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} />;
});

现在,如果父组件获得了对 MyInput 的引用,它将能够在其上调用 focusscrollIntoView 方法。但是,它将无法完全访问底层的 <input> DOM 节点。

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

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

  function handleClick() {
    ref.current.focus();
    // This won't work because the DOM node isn't exposed:
    // ref.current.style.opacity = 0.5;
  }

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


公开您自己的命令式方法

您通过命令式句柄公开的方法不必与 DOM 方法完全匹配。例如,这个 Post 组件通过命令式句柄公开了一个 scrollAndFocusAddComment 方法。这使得父级 Page 可以在您单击按钮时滚动评论列表*并*聚焦输入字段

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}>
        Write a comment
      </button>
      <Post ref={postRef} />
    </>
  );
}

陷阱

不要过度使用 refs。 您应该仅将 refs 用于无法表示为 props 的*命令式*行为:例如,滚动到节点、聚焦节点、触发动画、选择文本等。

如果您可以将某些内容表示为 prop,则不应使用 ref。 例如,与其从 Modal 组件公开命令式句柄(如 { open, close }),不如将 isOpen 作为 prop,如 <Modal isOpen={isOpen} />副作用可以帮助您通过 props 公开命令式行为。