预计都比拟相熟这些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])
  1. ref
    须要被赋值的ref对象。
  2. createHandle
    createHandle函数的返回值作为ref.current的值。
  3. [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官网里给出了几点应用倡议:

  1. 尽量避免命令式地给ref.current赋值,尽量采纳申明式的(即让React外部解决);
  2. forwardRef搭配应用
    这个不肯定,比方下面fomik库就没有这样做。

三、原理

先回顾下咱们之前是如何应用ref的:

  1. 期初利用ref拜访子组件的实例或则DOM元素;
  2. 起初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.

通过之前的常识咱们能够达成几点共识:

  1. ref.current赋值是个副作用,所以个别在Did函数或者事件处理函数里给ref.current赋值;
  2. 组件在卸载时要清理ref.current的值。

实质上useImperativeHandle就是在帮咱们做这些事件。

四、为什么须要useImperativeHandle

咱们都晓得父组件能够利用ref能够拜访子组件实例或者DOM元素,这其实相当于子组件向父组件输入自身实例或者DOM元素。而利用useImperativeHandle子组件能够向父组件输入任意数据。

整顿自GitHub笔记:useImperativeHandle