React Forwarding高阶组件传递Refs

在一般组件中使用Forwarding Refs

通常情况下,我们想获取一个组建或则一个HTML元素的实例通过 Ref特性 就可以实现,但是某些时候我们需要在子父级组建中传递使用实例,Forwarding Refs提供了一种技术手段来满足这个要求,特别是开发一些重复使用的组建库时。比如下面的例子:

function MyButton(props) {
  return (
    <button className="MyButton">
      {props.children}
    </button>
  );
}

上面的代码中MyButton组件渲染了一个HTML元素。对于使用者而言,React隐藏了将代码渲染成页面元素的过程,当其他组件使用MyButton时,并没有任何直接的方法来获取MyButton中的<button>元素,这样的设计方法有利于组建的分片管理,降低耦合。

但是像MyButton这样的组建,其实仅仅是对基本的HTML元素进行了简单的封装。某些时候,上层组建使用他时更希望将其作为一个基本的HTML元素来看待,实现某些效果需要直接操作DOM,比如focus、selection和animations效果。

下面的例子将Forwarding Refs添加到MyButton组件中,以实现实例传递的效果。

const MyButton = React.forwardRef((props, ref) => (
  <button ref={ref} className="MyButton">
    {props.children}
  </button>
));

// 通过ref可以直接操作<button>元素:
const ref = React.createRef();
<MyButton ref={ref}>Click me!</MyButton>;

这个时候,ref可以直接操作<button>元素。其实执行过程非常简单,也就下面5步:

  1. 通过React.createRef()方法创建一个ref实例。
  2. 和通常使用Ref一样,将其作为一个ref属性参数传递给MyButton组件。
  3. 使用React.forwardRef方法来创建一个组件,并将ref作为第二个参数传递。
  4. 将ref参数以ref属性的方式传递给<button>元素。
  5. 在渲染之后,可以使用ref.current来获取<button>元素的实例。

需要注意的是只有使用React.forwardRef来创建一个组件时,第二个ref参数才会存在。固定的方法或者使用类来创建组件并不会接收到ref参数。Forwarding Refs特性并不仅仅局限于用在HTML DOM元素上,这种方式也实用于组件之间传递Ref。 

在高阶组件中使用Forwarding Refs

高阶组件(HOCs)仅仅对一般组件的包装。一般组件被包装之后对于使用者来说并不清晰其是否是被包装过,此时使用Ref得到的是高阶组件的实例。因此Forwarding Refs特性对于高阶组件来说更有价值。

下面是一个高阶组件记录日志的例子:

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组件用于在每次数据更新前后记录props中的数据。我们用其包装前面的MyButton组件。

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

  render() {
    //
  }
}

export default logProps(MyButton);

此时通过import并使用Refs实际上得到的是LogProps的实例:

import FancyButton from './FancyButton';

const ref = React.createRef();
<MyButton
  label="Click Me"
  handleClick={handleClick}
  ref={ref}
/>;

我们使用Forwarding Refs对高阶组件进行简单的改造即可解决这个问题:

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;

      // 通过forwardedRef参数传递ref的值
      return <Component ref={forwardedRef} {...rest} />;
    }
  }
  
  //然后使用 React.forwardRef 来包装创建 LogProps组件的实例
  //注意这里使用 forwardedRef 来传递 父组件的 ref
  //
  return React.forwardRef((props, ref) => {
    return <LogProps {...props} forwardedRef={ref} />;
  });
}

开发调试组件名称显示

如果我们不进行任何调整,下面的代码在调试工具中输出的组件名称为:"ForwardRef(MyComonent)":

const WrappedComponent = React.forwardRef(
  function myFunction(props, ref) {
    return <LogProps {...props} forwardedRef={ref} />;
  }
);

可以通过displayName来设定想要现实的名字:

function logProps(Component) {
  class LogProps extends React.Component {
    // ...
  }

  //先定义返回的高阶组件方法
  function forwardRef(props, ref) {
    return <LogProps {...props} forwardedRef={ref} />;
  }

  //然后设定这个组件的名称
  const name = Component.displayName || Component.name;
  forwardRef.displayName = `logProps(${name})`;

  //构建组件
  return React.forwardRef(forwardRef);
}

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏MasiMaro 的技术博文

windows编程学习笔记(三)ListBox的使用方法

ListBox是Windows中的一种控件,一般被当做子窗口使用,Windows中所有子窗口都是通过发送一个通知码到父窗口父窗口通过WM_COMMAND消息接收...

12120
来自专栏向治洪

React Native之ViewPagerAndroid 组件

概述 今天我们来讲解一下关于 ViewPager 的使用,它是一个允许子视图左右滚动翻页的容器。我们知道在Android开发中系统有ViewPager这个组件,...

21480
来自专栏谈补锅

工作中碰到的js问题(disabled表单元素不能提交到服务器)

今天碰到一个奇葩的问题,asp页面表单提交后,有一个文本框<input type="text" name="phone" id="phone" />在后台获取不...

16420
来自专栏前端说吧

vue.js: 自定义事件之—— 子组件修改父组件的值

这里,相对本案例,父组件定义为Second-module,对应的子组件是Three-module

48740
来自专栏Flutter知识集

Flutter实现雨滴动画

写了几个Flutter的demo,但是对Flutter的自定义view和动画都不太了解,看到一个类似效果在android的实现,就尝试用Flutter做一下。同...

1.1K50
来自专栏非著名程序员

基础篇章:关于 React Native 之 ViewPagerAndroid 组件的讲解

今天我们来讲解一下关于 ViewPager 的使用,它是一个允许子视图左右滚动翻页的容器。而且每一个 ViewPagerAndroid 的子容器会被视作一个单独...

23250
来自专栏守候书阁

[边学边练]用简单实例学习React

学习之路不可停止,最近在玩 React。也动手尝试写了一个实例。借此整理总结下初步学习 React 的一些基础知识。因为这几天比较忙,没那么多时间,所以实例和文...

16060
来自专栏互联网杂技

编码规范

不要在自闭合(self-closing)元素的尾部添加斜线 -- HTML5 规范中明确说明这是可选的。 不要省略可选的结束标签(closing tag)(例如...

34570
来自专栏cnblogs

DOM事件第一弹

     近期温习了部分w3c上关于DOM事件的规范,发现以前有些模糊的概念更加清晰,以及受到罗胖(罗辑思维)的影响,很是想分享自己的了解的东西,希望大家给予指...

20660
来自专栏DannyHoo的专栏

iOS中在系统相册中创建自己App的自定义相册

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u010105969/article/details/...

24510

扫码关注云+社区

领取腾讯云代金券