React 高阶组件传递Forwarding Refs

简介: 在我们使用React高阶组件时,常常会在高阶组件上使用Refs遇到各种问题。最新版的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);
}
相关文章
|
1天前
|
前端开发 JavaScript 开发者
React 按钮组件 Button
本文介绍了 React 中按钮组件的基础概念,包括基本的 `&lt;button&gt;` 元素和自定义组件。详细探讨了事件处理、参数传递、状态管理、样式设置和可访问性优化等常见问题及其解决方案,并提供了代码示例。帮助开发者避免易错点,提升按钮组件的使用体验。
99 77
|
2天前
|
前端开发 UED 开发者
React 对话框组件 Dialog
本文详细介绍了如何在 React 中实现一个功能完备的对话框组件(Dialog),包括基本用法、常见问题及其解决方案,并通过代码案例进行说明。从安装依赖到创建组件、添加样式,再到解决关闭按钮失效、背景点击无效、键盘导航等问题,最后还介绍了如何添加动画效果和处理异步关闭操作。希望本文能帮助你在实际开发中更高效地使用 React 对话框组件。
96 75
|
7天前
|
前端开发 Java API
React 进度条组件 ProgressBar 详解
本文介绍了如何在 React 中创建进度条组件,从基础实现到常见问题及解决方案,包括动态更新、状态管理、性能优化、高级动画效果和响应式设计等方面,帮助开发者构建高效且用户体验良好的进度条。
35 18
|
22天前
|
存储 前端开发 测试技术
React组件的最佳实践
React组件的最佳实践
|
20天前
|
前端开发 API 开发者
React 文件上传组件 File Upload
本文详细介绍了如何在 React 中实现文件上传组件,从基础的文件选择和上传到服务器,再到解决文件大小、类型限制、并发上传等问题,以及实现多文件上传、断点续传和文件预览等高级功能,帮助开发者高效构建可靠的应用。
48 12
|
15天前
|
存储 前端开发 JavaScript
React 表单输入组件 Input:常见问题、易错点及解决方案
本文介绍了在 React 中使用表单输入组件 `Input` 的基础概念,包括受控组件与非受控组件的区别及其优势。通过具体代码案例,详细探讨了创建受控组件、处理多个输入字段、输入验证和格式化的方法,并指出了常见易错点及避免方法,旨在提升表单的健壮性和用户体验。
27 4
|
22天前
|
前端开发 JavaScript API
React 文件下载组件 File Download
本文介绍了在React中实现文件下载组件的方法,包括使用`a`标签和JavaScript动态生成文件,解决了文件路径、文件类型、大文件下载及文件名乱码等问题,并展示了使用第三方库`file-saver`和生成CSV文件的高级用法。
35 6
|
19天前
|
前端开发 JavaScript API
React 文件下载组件:File Download
本文详细介绍了如何在React应用中实现文件下载组件,包括基本概念、实现步骤和代码示例。同时,探讨了常见问题如文件类型不匹配、文件名乱码等及其解决方法,旨在提升用户体验和代码可维护性。
39 2
|
23天前
|
存储 前端开发 JavaScript
React 文件上传组件 File Upload
本文介绍了如何在 React 中实现文件上传组件,包括基本的概念、实现步骤、常见问题及解决方案。通过 `&lt;input type=&quot;file&quot;&gt;` 元素选择文件,使用 `fetch` 发送请求,处理文件类型和大小限制,以及多文件上传和进度条显示等高级功能,帮助开发者构建高效、可靠的文件上传组件。
65 2
|
24天前
|
存储 前端开发
在React框架中,如何使用对象来管理组件的状态
在React中,组件状态通过`state`对象管理,利用`setState`方法更新状态。状态变化触发组件重新渲染,实现UI动态更新。对象结构清晰,便于复杂状态管理。