目录

  • 前言
  • 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,嵌套多少层,一把梭就行了,不用改组件里边的代码。一个写好的组件,只需要做加法就好了。