目录
前言
react中,我们经常会想到,通过父元素直接调用子元素的方法,实现高内聚的效果。而这个用法的写法却是,五花八门。如下所示:
1、类组件中的使用React.createRef()
- 优点:
1、写法简单易懂 - 缺点:
1、假如子组件是嵌套了HOC,就无法指向真实子组件
import React , { Component } from "react"
class Child extends Component {
func(){
console.log("执行我")
}
render(){
return (<div>子组件</div>);
}
}
class Parent extends Component {
constructor(props) {
super(props);
this.Child = React.createRef();
}
handleOnClick = ()=>{
this.Child.current.func();
}
render(){
return (<div>
<button onClick={this.handleOnClick}>click</button>
<Child ref={this.Child}></Child>
</div>);
}
}
2、使用ref的函数式声明
- 优点:
1、写法更简单易懂
2、没有多余的代码 - 缺点:
1、假如子组件是嵌套了HOC,就无法指向真实子组件
import React , { Component } from "react"
class Child extends Component {
func(){
console.log("执行我")
}
render(){
return (<div>子组件</div>);
}
}
class Parent extends Component {
handleOnClick = ()=>{
this.Child.func();
}
render(){
return (<div>
<button onClick={this.handleOnClick}>click</button>
<Child ref={ node => this.Child = node }></Child>
</div>);
}
}
3、使用props自定义onRef属性
- 优点:
1、写法简单易懂
2、假如子组件是嵌套了HOC,也可以指向真实子组件 - 缺点:
1、需要自定义props属性
import React , { Component } from "react"
import { withRouter } from "react-router-dom"
// 使用装饰器给裹上一层高阶函数(装饰器需要安装对应的babel包,此处作为演示使用)
@withRouter
class Child extends Component {
componentDidMount(){
this.props.onRef && this.props.onRef(this);
}
func(){
console.log("执行我")
}
render(){
return (<div>子组件</div>);
}
}
class Parent extends Component {
handleOnClick(){
this.Child.func();
}
render(){
return (<div>
<button onClick={this.handleOnClick}>click</button>
<Child onRef={ node => this.Child = node }></Child>
</div>);
}
}
4、函数式和hooks写法
其实下面的缺点基本不算缺点了,因为函数式写法,下面算是简单的了。使用forwardRef只会让你的组件定义的更复杂
- 优点:
1、写法简单易懂
2、假如子组件嵌套了HOC,也可以指向真实子组件 - 缺点:
1、需要自定义props属性
2、需要自定义暴露的方法
父组件
import React from 'react';
import Child from './Child';
const Parent = () => {
let ChildRef = React.createRef();
function handleOnClick() {
ChildRef.current.func();
}
return (
<div>
<button onClick={handleOnClick}>click</button>
<Child onRef={ChildRef} />
</div>
);
};
export default Parent;
子组件
import React, { useImperativeHandle } from 'react';
import { withRouter } from 'react-router-dom';
const Child = props => {
//用useImperativeHandle暴露一些外部ref能访问的属性
useImperativeHandle(props.onRef, () => {
return {
func: func,
};
});
function func() {
console.log('执行我');
}
return <div>子组件</div>;
};
export default withRouter(Child);
5、使用forwardRef抛出子组件的ref
这个方法其实更适合自定义HOC。但问题是,withRouter、connect、Form.create等方法并不能抛出ref,假如Child本身就需要嵌套这些方法,那基本就不能混着用了。forwardRef本身也是用来抛出子元素,如input等原生元素的ref的,并不适合做组件ref抛出,因为组件的使用场景太复杂了。下面看代码实现:
父组件
import React from 'react';
import Child from './Child';
const Parent = () => {
let ChildRef = React.createRef();
function handleOnClick() {
ChildRef.current.func();
}
return (
<div>
<button onClick={handleOnClick}>click</button>
<Child ref={ChildRef} />
</div>
);
};
export default Parent;
子组件
import React, { Component } from 'react';
@Log
class Child extends Component {
func = () => {
console.log('打印了我');
};
render() {
return <div>我是个测试的子组件</div>;
}
}
// 自定义可以抛出子组件ref的HOC
function Log(Comp) {
const Log = props => {
const { forwardRef, ...rest } = props;
return <Comp ref={forwardRef} {...rest} />;
};
return React.forwardRef((props, ref) => {
return <Log {...props} forwardRef={ref} />;
});
}
export default Child;
总结
父组件调子组件函数有两种情况
- 子组件无HOC嵌套
- 有HOC嵌套
选择什么方法,具体情况具体分析。 使用onRef自定义props的方式更实用,无论组件是否需要嵌套HOC,嵌套多少层,一把梭就行了,不用改组件里边的代码。一个写好的组件,只需要做加法就好了。