React 钩子和功能组件参考

Ben*_*Ben 4 reactjs react-ref react-hooks

const Comp1 = forwardRef((props, ref) => {
    useImperativeHandle(ref, () => ({
    print: () => {
      console.log('comp1')
    }
  }), []);
  return <div>comp1</div>
});

const Comp2 = () => <div>comp2</div>;

const App = () => {
  const ref1 = useRef(null);
  const ref2 = useRef(null);

  useEffect(() => {
    console.log(ref1); // prints ref1 with the expected current  
    console.log(ref2); // prints ref2 with current: null
  })
  return <div><Comp1 ref={ref1}/><Comp2 ref={ref2}/></div>
}
Run Code Online (Sandbox Code Playgroud)
  1. Comp1 和 Comp2 参考之间有什么区别?
  2. 为什么我必须使用 forwardRef 和 useImperativeHandle 才能真正获得 Comp1 的引用?

https://codepen.io/benma/pen/mddEWjP?editors=1112

And*_*nko 5

反应文档说:

您不能在函数组件上使用 ref 属性,因为它们没有实例。(更多)

这意味着您不能将 refs 绑定到功能组件。这就是为什么你ref2.currentnull. 如果要将 ref 绑定到组件,则需要使用类组件。您ref1不是对Comp1组件的引用。它实际上包含一个你在useImperativeHandle钩子中传递的对象。即它包含下一个对象:

{
    print: () => {
      console.log('comp1')
    }
}
Run Code Online (Sandbox Code Playgroud)

forwardRef如果要将 ref 与组件呈现的某些 HTML 元素或类组件绑定,则必须使用功能组件。或者您可以使用useImperativeHandle钩子将您的 ref 与某个对象绑定。

更新

的使用与useImperativeHandle向类组件添加方法相同:

class Comp1 extends React.Component {
    print() {
        console.log('comp1');
    }

    render() {
        return (<div>comp1</div>)
    }
}
Run Code Online (Sandbox Code Playgroud)

是相同的

const Comp1 = forwardRef((props, ref) => {
    useImperativeHandle(ref, () => ({
    print: () => {
      console.log('comp1')
    }
  }), []);
  return <div>comp1</div>
});
Run Code Online (Sandbox Code Playgroud)

你在评论中问:

因此,在使用钩子(并且仍然避免使用类)之后,使用 ref 的唯一方法是使用useImperativeHandle(实际上是使用“假”引用)?这是一个好习惯吗?

回答:使用 ofuseImperativeHandle与在类组件中通过 refs 调用子组件方法是同样的坏习惯。React doc 说你应该避免通过 refs 调用子组件方法,你应该避免使用useImperativeHandle. 此外,您需要避免在没有它们的情况下可以做事情的地方使用 refs。