React ref 多场景使用教程(附demo)

2,811 阅读3分钟

React ref 多场景使用教程

React16.8 后,我们就不断的拥抱 Hook 开发,在组件化开发已经成为我们必不可缺的开发方式时,ref 的使用就显得尤为重要。

但是 class 开发依旧被大部分人所使用,所以我们来分析几种使用 ref 的场景。

下面存在几种场景:

  • 父组件: class, 子组件: class
  • 父组件: hook, 子组件: hook
  • 父组件: class, 子组件: hook
  • 父组件: hook, 子组件: class

上面场景如果在 dva 中使用会有问题,每种场景会对此进行分析

下面通过这几种场景分析:如何在 父组件 调用 子组件内部方法以及 数据

一、父组件: class, 子组件: class

1、子组件不使用 dva 情况

子组件不需要任何多余的操作, 直接编写父组件即可。

...
import { Button } from 'antd-mobile';
import { NoDvaSon } from './components'; // 不是用 dva 的子组件

@connect(({ classclass }) => ({ classclass }))
class ClassclassPage extends Component {
  noDvaChild: any;

  getSonEvent = () => {
    this.noDvaChild.noDvaSonEvent(); // 这里调用子组件的方法
    console.log(this.noDvaChild.state); // 这里获取子组件的 state 值
  };

  render() {
    return (
      <div>
        <Button onClick={this.getSonEvent}>调用子组件的方法</Button>
        <NoDvaSon
          ref={(e) => {
            this.noDvaChild = e;
          }}
        />
      </div>
    );
  }
}
export default ClassclassPage;

2、子组件使用 dva 情况

父组件一样按照上面的编写方式不变,只不过子组件加上了 dva,保存后打开页面,浏览器的 Console 就会有以下的警告。

这时我们点击父组件的按钮调用子组件的方法和数据时,就会报错。

接下来我们来修改 子组件的代码:

修改 connect 的字段:

const mapDispatchToProps = (dispatch, ownProps) => {
  return { dispatch };
};

@connect(({ classclass }) => ({ classclass }), mapDispatchToProps, null, { forwardRef: true }) // 新 react-redux 版本支持
@connect(({ classclass }) => ({ classclass }), mapDispatchToProps, null, { withRef: true }) // 旧 react-redux 版本支持

只要引入 forwardRef 或者 withRef 报错就迎刃而解。

在写 demo 时我使用的是 withRef 发现浏览器 Console 还是报错,并提示 withRef 不能使用,所以就换成了 forwardRef

二、父组件: hook, 子组件: hook

hook 中,使用 dva 不会影响到 ref 的使用。

父组件代码:

import React, { FC, useRef } from 'react';
import { Button } from 'antd-mobile';
import { NoDvaSon } from './components';

const HookhookPage: FC = () => {
  const childRef = useRef(); 

  const getSonEvent = () => {
    childRef.current.click(); // 调用子组件的方法
    console.log(childRef.current.value); // 获取子组件的数据
  };

  return (
    <div className={styles.center}>
      <Button onClick={getSonEvent}>调用子组件的方法</Button>
      {/* 子组件引入 cRef 这个名字可随便定义 */}
      <NoDvaSon cRef={childRef} /> 
    </div>
  );
}

export default connect(({ hookhook }) => ({ hookhook }))(HookhookPage);

子组件代码:

import React, { FC, useState, useImperativeHandle } from 'react'; // 引入需要的 useImperativeHandle

interface PageProps {
  cRef: any;
}

const Page: FC<PageProps> = ({ cRef }) => {
  const [noDvaValue, setNoDvaValue] = useState('noDvaValue');

  /**
   * 这里是重点!!!!
   * 此处注意useImperativeHandle方法的的第一个参数是目标元素的ref引用
   */
  useImperativeHandle(cRef, () => ({
    // click 就是暴露给父组件的方法
    click: () => {
      noDvaSonEvent();
    },
    value: { noDvaValue },
  }));

  const noDvaSonEvent = () => {
    console.log('this is no dva son event');
  };

  return <div>this is no dva son</div>;
};

export default Page;

三、父组件: class, 子组件: hook

这种情况下直接参考 二、父组件: hook, 子组件: hook 即可。将不在详细说明,demo 中也不做展示。

四、父组件: hook, 子组件: class

对于 class 类型的子组件来说,实现方法和 一、父组件: class, 子组件: class 差不多。这里附上代码供大家参考

对于不使用 dva 的的子组件来说,子组件不需要增加代码,只需要在父组件上编写即可:

import React, { FC, useRef } from 'react';
import { HookhookModelState, ConnectProps, connect } from 'alita';
import { Button } from 'antd-mobile';
import { NoDvaClassSon } from './components';

const HookhookPage: FC = ({ hookhook, dispatch }) => {

  let classChild: NoDvaClassSon | null = null;

  const getclassSonEvent = () => {
    classChild.noDvaSonEvent(); // 调用子组件的方法
    console.log(classChild.state); // 获取子组件的数据
  };

  return (
    <div className={styles.center}>
      <Button onClick={getclassSonEvent}>调用class子组件的方法</Button>
      <NoDvaClassSon
        ref={(e) => { // 使用 ref
          classChild = e;
        }}
      />
    </div>
  );
};

export default connect(({ hookhook }) => ({ hookhook }))(HookhookPage);

而使用 dva 的子组件则像 一、父组件: class, 子组件: class 加上 forwardRef: true 即可。

完整 demo 从这里进入哈,给个 star 多多支持

编写不易,点点关注给个赞呀~