ReactJS - useImperativeHandle Hook



useImperativeHandle 是 React 18 中引入的一个 React Hook。这个 Hook 允许我们自定义作为子组件 ref 公开的句柄。当我们需要立即与子组件连接时,这非常有用,这意味着我们希望立即访问和调用子组件上的方法或属性。

语法

useImperativeHandle(ref, createHandle, optional_dependency)

参数

  • ref − 这是一种特殊的工具,可以帮助我们与子组件连接。在使用 forwardRef 创建子组件时,它作为第二个参数接收。

  • createHandle − 这是关于我们想要对子组件执行的操作的指令集。这是我们从 useImperativeHandle 返回的内容。

  • 可选 _dependencies − 我们需要列出我们在 createHandle 部分中使用的所有内容(例如 props、state 和变量)。这样 React 就会检查这些内容,并确保它们不会意外更改。如果它们确实更改了,它将更新我们的特殊工具。

返回值

此方法返回 undefined。

如何使用它?

我们可以在组件的顶层使用 “useImperativeHandle” 来自定义它公开的 ref 句柄 −

import { forwardRef, useImperativeHandle } from 'react';

const MyComp = forwardRef(function MyComp(props, ref) {
   useImperativeHandle(ref, () => {
      return {
         // the methods we want in our code ...
      };
   }, []);

示例

因此,我们可以通过两种不同的方式使用此 Hook。首先是创建可用于父组件的自定义 ref 句柄,其次是公开我们自己的指令式方法。我们将进一步逐一讨论这两种方法。

向父组件提供自定义 ref 句柄

默认情况下,React 组件不提供对底层 DOM 元素的直接访问。我们将不得不使用 forwardRef 来允许父组件访问子组件中的 <input> DOM 节点。

import { forwardRef } from 'react';

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

此代码会将实际的 <input> DOM 节点返回给赋予 InputComp 的 ref。

有时我们不想公开完整的 DOM 节点,而只想公开其方法或属性的一部分。例如,在不公开整个 DOM 节点的情况下聚焦和滚动子组件。我们可以借助 useImperativeHandle Hook 来自定义公开的句柄。

例如

import { forwardRef, useImperativeHandle } from 'react';

const InputComp = forwardRef(function InputComp(props, ref) {
   useImperativeHandle(ref, () => ({
   focus() {
      inputRef.current.focus();
   },
   scrollIntoView() {
      inputRef.current.scrollIntoView();
   },
   }), []);
   
   return <input {...props} />;
});

在上面的示例中,我们正在修改父组件公开的句柄。父组件可以调用 InputComp 的 focus 和 scrollIntoView 方法。但它不会直接访问 DOM 节点 <input>。

示例 - 简短的应用程序来理解此 Hook

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

const Counter = forwardRef(function Counter(props, ref) {
   const [count, setCount] = useState(0);   
   const increment = () => {
      setCount(count + 1);
   };   
   const reset = () => {
      setCount(0);
   };   
   useImperativeHandle(ref, () => ({
      increment,
      reset,
   }), []);   
   return (
      <div>
         <p>Counter: {count}</p>
      </div>
   );
});

function App() {
   const counterRef = useRef();   
   const handleIncrement = () => {
      counterRef.current.increment();
   };
   
   const handleReset = () => {
      counterRef.current.reset();
   };
   
   return (
      <div>
         <Counter ref={counterRef} />
         <button onClick={handleIncrement}>Increment Count</button>
         <button onClick={handleReset}>Reset</button>
      </div>
   );
}
export default App;

输出

counter

示例 - 公开我们自己的指令式方法

在一个组件中,我们可以创建我们自己的自定义方法并将其提供给其父组件。这些自定义方法不必与 HTML 元素的内置方法相同。

假设我们有一个 InputForm 组件,它显示一个简单的输入字段组件 (InputForm),当按下按钮时,可以将其滚动到视图中并聚焦。当我们单击名为 App 的父组件中的按钮时,将执行输入字段上的此方法,使其滚动到视图中并获得焦点。

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

const InputForm = forwardRef((props, ref) => {
   const inputRef = useRef(null);
   
   useImperativeHandle(ref, () => ({
      scrollAndFocus() {
         inputRef.current.scrollIntoView();
         inputRef.current.focus();
      }
   }), []);
   
   return <input type="text" ref={inputRef} placeholder="Type here..." />;
});

function App() {
   const inputRef = useRef();
   
   const handleButtonClick = () => {
      inputRef.current.scrollAndFocus();
   };
   
   return (
      <div>
         <button onClick={handleButtonClick}>Scroll and Focus</button>
         <InputForm ref={inputRef} />
      </div>
   );
}

export default App;

在上面的示例中,我们演示了如何使用 forwardRef 和 useImperativeHandle 来滚动和聚焦输入字段。

总结

在 18 版中,useImperativeHandle 是一个有用的 React Hook,它允许通过更改 ref 来立即与子组件交互。当我们需要快速访问子组件的函数或属性时,它非常有用。通过使用此 Hook,我们可以创建连接并定义我们想要对子组件执行的操作,在需要时提供流畅的通信和更新,并且该方法返回 undefined。

reactjs_reference_api.htm
广告