react ref无法获取被高阶组件包装的原始组件问题

本文探讨了在React中遇到的问题:当使用ref无法直接获取被高阶组件(如dva connect)包装的原始组件。介绍了两种解决方案,包括使用getWrappedInstance()方法和透传refs。通过这些方法,可以在被包装组件上调用内部函数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题描述:

  • react无法通过ref获取被高阶组件包装的原始组件
  • 通过ref调用被dva connect包裹的组件报错

我们在平时使用ref获取一个组件的引用后,就可以直接通过ref调用组件自身的函数,但是对于高阶组件,使用ref调用原始组件的函数就会报错。

简介:

高阶组件(HOC)是 React 中用于复用组件逻辑的一种高级技巧。HOC 自身不是 React API 的一部分,它是一种基于 React 的组合特性而形成的设计模式。

简而言之:高阶组件是参数为组件,返回值为新组件的函数。

const EnhancedComponent = higherOrderComponent(WrappedComponent);

组件是将 props 转换为 UI,而高阶组件是将组件转换为另一个组件。

HOC 在 React 的第三方库中很常见,例如 Redux 的 connect 和 Relay 的 createFragmentContainer

 


虽然高阶组件的约定是将所有 props 传递给被包装组件,但这对于 refs 并不适用。那是因为 ref 实际上并不是一个 prop - 就像 key 一样,它是由 React 专门处理的。如果将 ref 添加到 HOC 的返回组件中,则 ref 引用指向容器组件,而不是被包装组件。

1. 当组件是被connect绑定redux后,可以使用getWrappedInstance()获取组件的原型

例子:

定义被connect包装的test类

import {connect} from 'react-redux'
import {Component} from 'react'
export default class Test extends Component {
  constructor(props) {
    super(props);
  }
  testFunction =(test='1')=>{
      alert(test)
  }
  render() {
    return (
      <div>test</div>
    )
  }
}
function mergeProps(stateProps, dispatchProps, ownProps) {
  return Object.assign({}, ownProps, stateProps, dispatchProps)
}
export default connect({}, {},mergeProps,{withRef:true})(Test)

 在组件加载完成后调用test组件内部testFunction函数

import {Component} from 'react'
import Test from './Test'
export default class Test2 extends Component {
  constructor(props) {
    super(props);
  }
  componentDidMount(){
	  //使用connect提供的getWrappedInstance()
      this.refs.test.getWrappedInstance().testFunction('test')
  }
  render() {
    return (
      <div><Test ref='test' /></div>
    )
  }
}

 

2. 使用 React.forwardRef 来获取传递给它的 ref,然后转发ref到它渲染的 DOM button

借用官网例子

function logProps(WrappedComponent) {
  class LogProps extends React.Component {
    componentDidUpdate(prevProps) {
      console.log('old props:', prevProps);
      console.log('new props:', this.props);
    }

    render() {
      return <WrappedComponent {...this.props} />;
    }
  }

  return LogProps;
}

“logProps” HOC 透传(pass through)所有 props 到其包裹的组件,所以渲染结果将是相同的。

class FancyButton extends React.Component {
  focus() {
    // ...
  }

  // ...
}

// 我们导出 LogProps,而不是 FancyButton。
// 虽然它也会渲染一个 FancyButton。
export default logProps(FancyButton);

有一点需要注意:refs 将不会透传下去。这是因为 ref 不是 prop 属性。就像 key 一样,其被 React 进行了特殊处理。如果你对 HOC 添加 ref,该 ref 将引用最外层的容器组件,而不是被包裹的组件。

这意味着用于我们 FancyButton 组件的 refs 实际上将被挂载到 LogProps 组件:

import FancyButton from './FancyButton';

const ref = React.createRef();

// 我们导入的 FancyButton 组件是高阶组件(HOC)LogProps。
// 尽管渲染结果将是一样的,
// 但我们的 ref 将指向 LogProps 而不是内部的 FancyButton 组件!
// 这意味着我们不能调用例如 ref.current.focus() 这样的方法
<FancyButton
  label="Click Me"
  handleClick={handleClick}
  ref={ref}
/>;

幸运的是,我们可以使用 React.forwardRef API 明确地将 refs 转发到内部的 FancyButton 组件。React.forwardRef 接受一个渲染函数,其接收 props 和 ref 参数并返回一个 React 节点。

function logProps(Component) {
  class LogProps extends React.Component {
    componentDidUpdate(prevProps) {
      console.log('old props:', prevProps);
      console.log('new props:', this.props);
    }

    render() {
      const {forwardedRef, ...rest} = this.props;

      // 将自定义的 prop 属性 “forwardedRef” 定义为 ref
      return <Component ref={forwardedRef} {...rest} />;
    }
  }

  // 注意 React.forwardRef 回调的第二个参数 “ref”。
  // 我们可以将其作为常规 prop 属性传递给 LogProps,例如 “forwardedRef”
  // 然后它就可以被挂载到被 LogProps 包裹的子组件上。
  return React.forwardRef((props, ref) => {
    return <LogProps {...props} forwardedRef={ref} />;
  });
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值