预计都比拟相熟这些HOOKS了吧:useState
, useEffect
, useContext
, useMemo
。但我当第一次看到useImperativeHandle
时,一脸懵逼(这是什么鬼货色~~~)。
一、是什么?
React官网对useImperativeHandle
介绍也比拟简短。总结一句话就是:子组件利用useImperativeHandle
能够让父组件输入任意数据。
// FancyInput组件作为子组件const FancyInput = React.forwardRef(function FancyInput(props, ref) { const inputRef = useRef(); // 命令式的给`ref.current`赋值个对象 useImperativeHandle(ref, () => ({ focus: () => { inputRef.current.focus() } })); return <input ref={inputRef} ... />})// Example组件作为父组件function Example() { const fancyInputRef = useRef() const focus = () => { fancyInputRef.current.focus() } return ( <> <FancyInput ref={fancyInputRef} /> </> )}
二、怎么用?
2.1 语法
useImperativeHandle(ref, createHandle, [deps])
ref
须要被赋值的ref
对象。createHandle
:createHandle
函数的返回值作为ref.current
的值。[deps]
依赖数组,依赖发生变化会从新执行createHandle
函数。
2.2 进阶:什么时候执行createHandle
函数?
测试发现和useLayoutEffect
执行机会统一。
批改下组件FancyInput
内容:
const FancyInput = React.forwardRef(function FancyInput(props, ref) { const inputRef = useRef(); console.log('render 1') useLayoutEffect(() => { console.log('useEffect1', ref) }) useImperativeHandle(ref, function() { debugger console.log('useImperativeHandle') return { focus: () => { inputRef.current.focus(); } } }) useLayoutEffect(() => { console.log('useEffect2', ref); }) console.log('render 2') return <input ref={inputRef} placeholder="FancyInput"/>;})
看看控制台输入发现createHandle
函数的执行机会和useLayoutEffect
统一,这样就保障了在任意地位的useEffect
里都能拿到最新的ref.current
的值。
留神:执行createHandle
函数的还有个前提条件,即useImperativeHandle
的第一个实参ref
必须有值(否则执行createHandle
函数也没意义啊)。
2.3 利用场景
目前我的项目曾经有多处应用场景了,次要是解决父组件获取子组件的数据或者调用子组件的里申明的函数。
如formik库的一处应用:
React.useImperativeHandle(innerRef, () => formikbag);
2.4 最佳实际
React官网里给出了几点应用倡议:
- 尽量避免命令式地给
ref.current
赋值,尽量采纳申明式的(即让React外部解决); - 和
forwardRef
搭配应用
这个不肯定,比方下面fomik库就没有这样做。
三、原理
先回顾下咱们之前是如何应用ref
的:
- 期初利用
ref
拜访子组件的实例或则DOM元素; - 起初
useRef
呈现了,咱们在函数组件里利用useRef
还能够存储一些相似成员变量的数据。
再回顾下React如何解决申明式ref的:
React will assign the current property with the DOM element when the component mounts, and assign it back to null when it unmounts.
通过之前的常识咱们能够达成几点共识:
- 给
ref.current
赋值是个副作用,所以个别在Did
函数或者事件处理函数里给ref.current
赋值; - 组件在卸载时要清理
ref.current
的值。
实质上useImperativeHandle
就是在帮咱们做这些事件。
四、为什么须要useImperativeHandle
咱们都晓得父组件能够利用ref
能够拜访子组件实例或者DOM元素,这其实相当于子组件向父组件输入自身实例或者DOM元素。而利用useImperativeHandle
子组件能够向父组件输入任意数据。
整顿自GitHub笔记:useImperativeHandle