useImperativeHandle
大约 2 分钟约 474 字
有时你需要暴露一个方法给父组件,让父组件可以主动与子组件交互。这可以通过 ref
来实现,useRef
钩子可以让你拥有一个与组件实例关联的对象,并且不会改变时触发组件重新渲染。
父组件可以传递一个 ref
给子组件,然后子组件可以在这个 ref
上挂载方法,父组件可以调用这些方法。
type InputAPI = { focusInput: () => void }
function MyInput({
ref,
...props
}: React.InputHTMLAttributes<HTMLInputElement> & {
ref: React.RefObject<InputAPI>
}) {
const inputRef = useRef()
ref.current = {
focusInput: () => inputRef.current.focus(),
}
return <input ref={inputRef} {...props} />
}
function App() {
const myInputRef = useRef<InputAPI>(null)
return (
<div>
<MyInput ref={myInputRef} placeholder="Enter your name" />
<button onClick={() => myInputRef.current.focusInput()}>
Focus the input
</button>
</div>
)
}
这实际上是可行的,但是当在 React 的 concurrent/suspense 特性中使用时,会出现一些边缘情况的问题(它也不支持回调 ref)。所以,我们使用 useImperativeHandle
钩子来实现这个功能:
type InputAPI = { focusInput: () => void }
function MyInput({
ref,
...props
}: React.InputHTMLAttributes<HTMLInputElement> & {
ref: React.RefObject<InputAPI>
}) {
const inputRef = useRef()
useImperativeHandle(
ref,
() => ({ focusInput: () => inputRef.current.focus() }),
[],
)
return <input ref={inputRef} {...props} />
}
useImperativeHandle
接收三个参数:
ref
:与组件实例关联的ref
对象。createHandle
:一个函数,返回一个对象,这个对象就是暴露给父组件的 API。dependencies
:依赖数组,当依赖数组中的值发生变化时,createHandle
函数会重新执行。
Why you shouldn't put refs in a dependency array.
useImperativeHandle
允许我们暴露一些方法给父组件,这些方法可以被父组件调用。这在某些场景下非常有用,比如你有一个需要手动操作的组件,但是你又不想让父组件直接操作这个组件的内部状态。
::: TIP
大多数情况下,你不需要 useImperativeHandle
。在决定使用它之前,问问自己是否还有其他方式可以实现你的目标。
Imperative vs Declarative Programming
:::