目录
- 前言
- 1、类组件中的使用`React.createRef()`
- 2、使用ref的函数式声明
- 3、使用props自定义onRef属性
- 4、函数式和hooks写法
- 5、函数式编程使用`forwardRef`抛出子组件的`ref`
- 总结
前言
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
很简洁的方法
import {
forwardRef,
useState,
useCallback,
useImperativeHandle,
createRef
} from "react";
export default function App() {
let c = createRef(null);
const handleChick = useCallback(() => {
console.log("父组件点击了");
c.current.add();
}, [c]);
return (
<div className="App">
<button onClick={handleChick}>点我运行子组件方法</button>
<Child ref={c} />
</div>
);
}
const Child = forwardRef((props, ref)=> {
const [num, setNum] = useState(0);
useImperativeHandle(
ref,
() => ({ add })
);
const add = () => {
setNum((num) => ++num);
};
return (
<div>
<button onClick={add}>这是子组件自己调用</button>
子组件的值:{num}
</div>
);
});
总结
父组件调子组件函数有两种情况
- 子组件无HOC嵌套
- 有HOC嵌套
选择什么方法,具体情况具体分析。 使用onRef
自定义props
的方式更实用,无论组件是否需要嵌套HOC,嵌套多少层,一把梭就行了,不用改组件里边的代码。一个写好的组件,只需要做加法就好了。