class组件与函数组件之间ref传递及HOC组件ref传递方法

4,957 阅读3分钟

class组件传ref到函数组件

在日常开发过程中,我们经常需要使用ref,来做一些逻辑处理。对于当前组件来说,ref的使用比较方便,而对于跨组件使用,尤其是现在hooks的广泛运用和以前的class的交叉使用,有时候会让我们使用上出现一些问题。比如在父组件中引入子组件,把ref传入到函数子组件中,会一直为空

父组件

class Home extends React.Component {
	childRef = null;
	render() {
		return (
			<div>
				<Test ref={ref => this.childRef = ref} />
				<Button onClick={() => {
						console.log(this.childRef)
					
				}}>
					测试调用子组件方法
				</Button>
			</div>
		)
	}
}

子组件-函数组件

const ForwardRef = (props) => {
  console.log(props)

  return (
    <div>
      ForwardRef组件
    </div>
  )
}


export default ForwardRef;

子组件-class组件


class ForwardRef extends React.Component {

  test = () => {
    console.log('我是ForwardRef组件的test方法')
  }
  render(){
    return (
      <div>
        ForwardRef组件
      </div>
    )

  }

}


export default ForwardRef;

如上可知,在class组件中传递ref给函数组件,接收到的都是undefined,而class组件是能接收父组件传过来的ref的。那怎么能把ref从class父组件传递给函数子组件呢?react的useImperativeHandleforwardRef

class Home extends React.Component {
	childRef = null;
	render() {
		return (
			<div>
				<Test ref={ref => this.childRef = ref} />
				<Button onClick={() => {
					if(this.childRef){
						console.log(this.childRef.test())
					}
				}}>
					测试调用子组件方法
				</Button>
			</div>
		)
	}
}

export default connect()(Home);
import React, {
  useImperativeHandle,
  forwardRef
} from 'react';

const ForwardRef = forwardRef((props, ref) => {
  console.log(props)

  useImperativeHandle(ref, () => ({
    test,
  }));
  const test = () => {
    console.log('我是ForwardRef组件的test方法')
  }

  return (
    <div>
      ForwardRef组件
    </div>
  )
})


export default ForwardRef;

这样就可以在class组件中通过ref调用函数子组件的方法了。应用场景比如是在父组件中调用子组件的表单重置(有的侧边栏、弹框的单独表单验证,页面功能太多的时候,建议不同form验证放在不同的子组件。)

HOC组件ref传递(2020.09.28更新)

在react项目中,我们经常会用到HOC组件,比如为了页面出错而不至于全局崩溃的error包装组件,每个组件出错只把当前组件出错显示,而整体页面不至于崩溃。比如侧边栏、弹窗、tab切换等打开的时候,如果当前组件崩溃,只显示打印当前出错的组件显示部位,用户体验不会太差。还有我们经常使用的全局数据管理,比如dva的connect包装组件,使得组件可以使用dva的全局状态。 当前父组件代码如下:

import React from 'react';
import { connect } from 'dva';
import { Button } from 'antd';
import Test from './component/forwardRef';


class Home extends React.Component {
	childRef = null;
	render() {
		return (
			<div>
				<Test ref={ref => this.childRef = ref} />
				<Button onClick={() => {
					if(this.childRef){
						console.log(this.childRef)
					}
				}}>
					测试调用子组件方法
				</Button>
			</div>
		)
	}
}

export default connect()(Home);

子组件为函数组件,被connect包装。

import React, {
} from 'react';
import { connect } from 'dva';

const ForwardRef = (props) => {
  console.log(props)
  const test = () => {
    console.log('我是ForwardRef组件的test方法')
  }

  return (
    <div>
      ForwardRef组件
    </div>
  )
}

const ForwardRefWrap = connect()(ForwardRef) 

export default ForwardRefWrap;

此时我们在父组件通过ref调用子组件的时候,会发现打印的其实是子组件被connect包装的HOC组件,无法获取我们真实想要的子组件,更别说调用子组件的方法了。 那我们该如何才能调用函数子组件的内部方法呢?和普通函数组件一样的调用可行吗?试一试就知道啦!

import React, {
    useImperativeHandle,
    forwardRef
} from 'react';
import { connect } from 'dva';

const ForwardRef = forwardRef((props, ref) => {
  console.log(props)
  console.log(ref)
  useImperativeHandle(ref, () => ({
    test,
  }));
  const test = () => {
    console.log('我是ForwardRef组件的test方法')
  }

  return (
    <div>
      ForwardRef组件
    </div>
  )
})

const ForwardRefWrap = connect()(ForwardRef) 

export default ForwardRefWrap;

代码保存,运行。。。。。。芜湖起飞!!!!!!!! 直接报错!!!看来是不能这么用了,那么该如何使用呢?我们试试下面的方法

父组件不做修改,子组件改成下面这种,因为forwardRef函数是作为把父组件的ref传递给子组件中,并且进行包装,props和ref进行分开传递,所以应该是connect进行包装,forwardRef在最外层进行包装,才能把父组件的ref传入到子组件中。


import React, {
  useImperativeHandle,
  forwardRef
} from 'react';
import { connect } from 'dva';

const ForwardRef = (props) => {
  console.log(props)
  const {refInstance} = props;

  useImperativeHandle(refInstance, () => ({
    test,
  }));
  const test = () => {
    console.log('我是ForwardRef组件的test方法')
  }

  return (
    <div>
      ForwardRef组件
    </div>
  )
}

const ForwardRefWrap = connect()(ForwardRef) 

export default forwardRef((props, ref) => <ForwardRefWrap {...props} refInstance={ref} />);

这样我们就能通过ref在父组件中调用HOC包裹的函数子组件的各种方法和状态了,现在终于可以愉快的玩耍了

完结撒花~~~~~~~~~~~