我们习惯对类组件使用ref,指向类组件实例。我们可以调用类组件实例上面的方法。
import React from 'react';class Child extends React.Component {log() {console.log('test');}render() {return null;}}class App extends React.Component {childEl = React.createRef();componentDidMount() {this.childEl.current.log();}render() {return <Child ref={this.childEl} />;}}export default App;
组件有些特定属性是不会放在props中的,比如key,比如ref。
比如下面的类组件中从props获取ref会warn。这是因为 ref 不是 prop 属性。就像 key 一样,其被 React 进行了特殊处理。
import React from 'react';class Child extends React.Component {componentDidMount() {console.log(this.props.ref);}render() {return null;}}class App extends React.Component {childEl = React.createRef();render() {return <Child ref={this.childEl} />;}}export default App;

所以你无法从props中获取ref属性。
另外如果我们给函数式组件设置ref属性会提示warning。
import React from 'react';const Child = () => null;class App extends React.Component {childEl = React.createRef();render() {return <Child ref={this.childEl} />;}}export default App;

而且函数式组件也没法挂载方法。
这也有问题,那也有问题,听起来不太妙。不过还是有解决办法。
问题:如果我们有一个子组件是函数式组件,想调用它的方法应该怎么办?
首先应该让组件接收到ref属性。
如果你希望给子组件传递ref,那就需要用到React.forwardRef方法,即转发ref。
例如
import React from 'react';const Child = React.forwardRef((props, ref) => (<input ref={ref} />));class App extends React.Component {childEl = React.createRef();componentDidMount() {this.childEl.current.focus();}render() {return <Child ref={this.childEl} />;}}export default App;
上面这段代码中,App给子组件Child传ref参数,Child被React.forwardRef包装,对ref属性处理后传递给里面的函数(第二个参数),这样函数组件就可以获取到ref属性了。
(函数式组件传递ref给自己的子元素,让父组件可以获取子元素的引用是forwardref比较常见的应用场景)
函数式组件有了ref方法,那么应该让ref上面绑定上方法以便父组件调用呢?
答案是使用useImperativeHandle钩子对接收到的ref属性进行处理,给它挂载上一些方法。
看下面的例子
import React, {useImperativeHandle} from 'react';const Child = React.forwardRef((props, ref) => {useImperativeHandle(ref, () => ({log: () => {console.log('test');}}));return null;});class App extends React.Component {childEl = React.createRef();componentDidMount() {this.childEl.current.log();}render() {return <Child ref={this.childEl} />;}}export default App;
这个例子实现了和最开始的类组件相同的效果。
总结:如果习惯使用函数式组件,还有调用组件方法的需求,那么可以使用React.forwardRef+ useImperativeHandle来实现。
