useImperativeHandle 是 React 中的一个相对高级且强大的 Hook. 当我们想要在父组件中,调用自定义子组件内部的方法时,就需要使用到这个 Hook.
1useImperativeHandle(ref, createHandle, deps?)
通常,我们会通过 ref 来获取子组件的引用,例如,默认的 input 组件,我们可以通过 ref 来获取其引用,然后调用其方法
01import { useRef } from "react"0203export default function Demo01() {04const inputRef = useRef<HTMLInputElement>(null);0506function __clickHandler() {07inputRef.current?.focus();08}0910return (11<div className="flex gap-4 p-4">12<input ref={inputRef} className="grow" />13<button onClick={__clickHandler} className="button">点击获取焦点</button>14</div>15);16}
但是许多时候, 直接使用 input 标签并不能满足我们的需求, 我们需要基于 input 做额外的封装. 但是封装之后, 我们还是希望能通过调用 .focus 让输入框获取焦点.
1const input = useRef(null)2...3<Input ref={input} type='text' />4...5input.current.focus()
我们使用封装之后的 Input, 无法直接拿到内部的 input 对象, 但是我们的目标依然是获取 input 对象, 然后调用 focus,这里就需要借助 forwardRef 与 useImperativeHandle 来实现
在 React 19 中,我们不再使用
forwardRef来实现 ref 的传递,请参考 React 19 中 ref 机制更改, forwardRef 被无情抛弃
在之前的版本中,forwardRef 能够帮助 React 组件传递 ref. 或者说是内部对象控制权的转移与转发. 它接收一个组件作为参数,然后返回一个可渲染的函数组件
1// 注意 ref 的传递方式2function MyInput(props, ref) {3return (4<input ref={ref} {...props} />5)6}78const Input = forwardRef(MyInput)
自定义 Input 组件的使用与代码如下所示
01import { useRef } from "react"02import Input from "./Input"03export default function Demo01() {04const inputRef = useRef<HTMLInputElement>(null);0506function __clickHandler() {07inputRef.current?.focus();08}0910return (11<div className="flex gap-4 p-4">12<Input ref={inputRef} className="grow" />13<button onClick={__clickHandler} className="button">点击获取焦点</button>14</div>15);16}
当我们想要自定义子组件的内部方法时,除了需要借助 forwardRef 来传递 ref 之外,还需要借助 useImperativeHandle 来在子组件内部自定义方法
1useImperativeHandle(ref, createHandle, deps?)
ref 引用在下面的案例中,我们使用 useImperativeHandle 在子组件内部定义了弹窗的打开与关闭方法,并将其暴露给父组件,父组件可以通过 ref 来调用这些方法
01import { useRef } from 'react';02import Modal, { ElegantModalHandle } from './modal';0304export default function App() {05const modalRef = useRef<ElegantModalHandle>(null);0607return (08<div className="flex items-center justify-center p-8 gap-4">09<button onClick={() => modalRef.current?.open()} className="button">10打开弹窗11</button>12<Modal ref={modalRef} />13</div>14);15}
在下面的案例中,我们实现了输入框的振动反馈。没有新的知识点,仅用于扩展实践场景,大家直接看代码学习
01import { useRef } from 'react';02import CustomInput, { CustomInputHandle } from './input';0304export default function FormPage() {05const inputRef = useRef<CustomInputHandle>(null);0607return (08<div className="flex flex-col items-center justify-center px-4 py-8">09<div className="w-full max-w-sm space-y-4">10<CustomInput ref={inputRef} />11<div className="flex gap-4 justify-end">12<button onClick={() => inputRef.current?.focus()} className="button">13获取焦点14</button>15<button onClick={() => inputRef.current?.shake()} className="button border-red-500 text-red-500">16触发错误抖动17</button>18</div>19</div>20</div>21);22}
useImperativeHandle 在封装组件时,可以让我们在子组件内部定义方法,并将其暴露给父组件,父组件可以通过 ref 来调用这些方法,从而非常方便的控制子组件的行为。这样的特性对自定义组件非常有用,在 React 19 中,对于 forwardRef 的使用有一些改动,我在React 19 中 ref 机制更改, forwardRef 被无情抛弃中已经进行了详细的介绍,你也可以进一步学习