我们习惯对类组件使用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
来实现。