Live2D

React学习笔记(九) forwardRef和useImperativeHandle

forwardRef

ref 是为了获取某个节点的实例,但是函数式组件(PureComponent)是没有实例的,不存在 this 的,这种时候是拿不到函数式组件的 ref 的。为了解决这个问题,由此引入 React.forwardRefReact.forwardRef 允许某些组件接收 ref,并将其向下传递给子组件

基本使用如下

const Input = forwardRef((props, ref) => {
  const inputRef = useRef(null)
  return (
    <div ref={inputRef}>hello</div>
  )
})

export default function Index() {
  const ipt = useRef(null)
  const handleClick = useCallback(() => ipt.current.focus(), [ ipt ]);
    
  return (
    <div>
      <Input ref={ipt}></Input>
    </div>
  )
}

useImperativeHandle

useImperativeHandle 和 React.forwardRef 必须是配合使用的。useImperativeHandle 可以让你在使用 ref 时自定义暴露给父组件的实例值

useImperativeHandle(ref, createHandle, [deps])

​ ref:定义 current 对象的 ref createHandle:一个函数,返回值是一个对象,即这个 ref 的 current
​ 对象 [deps]:即依赖列表,当监听的依赖发生变化,useImperativeHandle 才会重新将子组件的实例属性输出到父组件
​ ref 的 current 属性上,如果为空数组,则不会重新输出。

一个关于 ref 转发的例子

import React, {
  useState,
  useRef,
  useImperativeHandle,
  useCallback
} from 'react';
import ReactDOM from 'react-dom';

const FancyInput = React.forwardRef((props, ref) => {
  const [ fresh, setFresh ] = useState(false)
  const attRef = useRef(0);
  useImperativeHandle(ref, () => ({
    attRef,
    fresh
  }), [ fresh ]);

  const handleClick = useCallback(() => {
    attRef.current++;
  }, []);

  return (
    <div>
      {attRef.current}
      <button onClick={handleClick}>Fancy</button>
      <button onClick={() => setFresh(!fresh)}>刷新</button>
    </div>
  )
});

const App = props => {
  const fancyInputRef = useRef();

  return (
    <div>
      <FancyInput ref={fancyInputRef} />
      <button
        onClick={() => console.log(fancyInputRef.current)}
      >父组件访问子组件的实例属性</button>
    </div>
  )
}

ReactDOM.render(<App />, root);

通过 useImperativeHandle 将子组件的实例属性输出到父组件,而子组件内部通过 ref 更改 current 对象后,组件不会重新渲染,需要改变 useState 设置的状态才能更改。

posted @ 2021-08-24 17:45  吃完夜宵再睡觉  阅读(768)  评论(0)    收藏  举报