探索在 React 中引入高阶组件的最佳实践

探索在 react 中引入高阶组件的最佳实践

这是高阶组件系列的第三部分。在第一个教程中,我们从零开始。我们学习了 ES6 语法、高阶函数和高阶组件的基础知识。

高阶组件模式对于创建抽象组件非常有用 – 您可以使用它们与现有组件共享数据(状态和行为)。在本系列的第二部分中,我演示了使用此模式的代码的实际示例。这包括受保护的路由、创建可配置的通用容器、将加载指示器附加到组件等。

在本教程中,我们将了解您在编写 HOC 时应该考虑的一些最佳实践和注意事项。

简介

React 之前有一个名为 Mixins 的东西,它与 React.createClass 方法配合得很好。 Mixins 允许开发人员在组件之间共享代码。然而,它们有一些缺点,这个想法最终被放弃了。 Mixin 没有升级为支持 ES6 类,Dan Abramov 甚至写了一篇深入的文章来解释为什么 Mixin 被认为是有害的。

高阶组件作为 Mixins 的替代品出现,并且它们支持 ES6 类。此外,HOC 不需要对 React API 做任何事情,并且是一种与 React 配合良好的通用模式。然而,HOC 也有缺陷。尽管高阶组件的缺点在较小的项目中可能并不明显,但您可以将多个高阶组件链接到单个组件,如下所示。

const SomeNewComponent =         withRouter(RequireAuth(LoaderDemo(GenericContainer(CustomForm(Form)))))

您不应该让链接发展到您问自己这样的问题:“这些道具从哪里来?”本教程解决了高阶组件模式的一些常见问题以及正确解决这些问题的解决方案。

HOC 的问题

与 HOC 相关的一些常见问题与 HOC 本身关系不大,而与它们的实现有关。

正如您所知,HOC 非常适合代码抽象和创建可重用代码。然而,当你堆叠了多个 HOC 时,如果某些东西看起来不合适或者某些 props 没有显示,那么调试起来就会很痛苦,因为 React DevTools 为你提供的有关可能出现问题的线索非常有限。

现实世界的 HOC 问题

为了了解 HOC 的缺点,我创建了一个示例演示,其中嵌套了我们在上一教程中创建的一些 HOC。我们有四个高阶函数包装单个 ContactList 组件。如果代码没有意义或者您没有遵循我之前的教程,这里是其工作原理的简短摘要。

withRouter 是一个 HOC,是 React-router 包的一部分。它使您可以访问历史对象的属性,然后将它们作为道具传递。

withAuth 查找 authentication 属性,如果身份验证为 true,则呈现 WrappedComponent。如果身份验证为 false,则会将 ‘/login‘ 推送到历史对象。

withGenericContainer 除了 WrappedComponent 之外还接受一个对象作为输入。 GenericContainer 进行 API 调用并将结果存储在状态中,然后将数据作为 props 发送到包装的组件。

withLoader 是一个附加加载指示器的 HOC。指示器旋转,直到获取的数据达到状态。

BestPracticeDemo.jsx

class BestPracticesDemo extends Component {    render() {return(            
)}}const ContactList = ({contacts}) => {return(
    {contacts.map( (contact) =>
  • presentation

    {contact.name}

    {contact.email}
    {contact.phone}
  • )}
)}const reqAPI = {reqUrl: 'https://demo1443058.mockable.io/users/', reqMethod:'GET', resName:'contacts'}const ExtendedContactList = withRouter( withAuth( withGenericContainer(reqAPI)( withLoader('contacts') (ContactList))));export default BestPracticesDemo;

现在您可以亲眼看到高阶组件的一些常见陷阱。让我们详细讨论其中的一些内容。

基本注意事项

不要忘记在 HOC 中传播道具

假设我们在组合层次结构的顶部有一个 authenticated = { this.state.authenticated } 属性。我们知道这是一个重要的道具,并且应该将其一直延伸到演示组件。然而,想象一下中间 HOC,例如 withGenericContainer,决定忽略它的所有 props。

//render method of withGenericContainerrender() {return(    )}

这是一个非常常见的错误,在编写高阶组件时应该尽量避免。不熟悉 HOC 的人可能会发现很难弄清楚为什么所有的 props 都丢失了,因为很难隔离问题。所以,永远记住在你的 HOC 中传播 props。

//The right wayrender() {return()}

不要传递超出 HOC 范围的不存在的 props

HOC 可能会引入 WrappedComponent 可能没有任何用处的新属性。在这种情况下,传递仅与组合组件相关的 props 是一个很好的做法。

高阶组件可以通过两种方式接受数据:作为函数的参数或作为组件的 prop。例如, authenticated = { this.state.authenticated } 是一个 prop 示例,而在 withGenericContainer(reqAPI)(ContactList) 中,我们传递数据作为参数。

因为 withGenericContainer 是一个函数,所以您可以根据需要传入任意数量的参数。在上面的示例中,配置对象用于指定组件的数据依赖性。然而,增强组件和包装组件之间的契约是严格通过 props 进行的。

因此,我建议通过函数参数填充静态时间数据依赖项,并将动态数据作为 props 传递。经过身份验证的道具是动态的,因为用户可以通过身份验证,也可以不通过身份验证,具体取决于他们是否登录,但我们可以确定 reqAPI 对象的内容不会动态更改。

不要在渲染方法中使用 HOC

这是一个您应该不惜一切代价避免的示例。

var OriginalComponent = () => 

Hello world.

;class App extends React.Component { render() { return React.createElement(enhanceComponent(OriginalComponent)); }};

除了性能问题之外,您还将在每次渲染时丢失 OriginalComponent 及其所有子组件的状态。要解决这个问题,请将 HOC 声明移到 render 方法之外,使其仅创建一次,以便渲染始终返回相同的EnhancedComponent。

var OriginalComponent = () => 

Hello world.

;var EnhancedComponent = enhanceComponent(OriginalComponent);class App extends React.Component { render() { return React.createElement(EnhancedComponent); }};

不要改变包装组件

改变 HOC 内的包装组件将导致无法在 HOC 外部使用包装组件。如果您的 HOC 返回 WrappedComponent,您几乎总是可以确定自己做错了。下面的例子演示了突变和组合之间的区别。

纳米搜索 纳米搜索

纳米搜索:360推出的新一代AI搜索引擎

纳米搜索 30 查看详情 纳米搜索

function logger(WrappedComponent) { WrappedComponent.prototype.componentWillReceiveProps = function(nextProps) {    console.log('Current props: ', this.props);    console.log('Next props: ', nextProps);  };  // We're returning the WrappedComponent rather than composing  //it  return WrappedComponent;}

组合是 React 的基本特征之一。您可以在其渲染函数中将一个组件包装在另一个组件内,这就是所谓的组合。

function logger(WrappedComponent) {  return class extends Component {    componentWillReceiveProps(nextProps) {      console.log('Current props: ', this.props);      console.log('Next props: ', nextProps);    }    render() {      // Wraps the input component in a container, without mutating it. Good!      return ;    }  }}

此外,如果您改变 HOC 内的 WrappedComponent,然后使用另一个 HOC 包装增强组件,则第一个 HOC 所做的更改将被覆盖。为了避免这种情况,您应该坚持组合组件而不是改变它们。

命名空间通用 Propnames

当您有多个堆叠时,命名空间道具名称的重要性是显而易见的。组件可能会将 prop 名称推送到已被另一个高阶组件使用的 WrappedComponent 中。

import React, { Component } from 'react';const withMouse = (WrappedComponent) => {  return class withMouse extends Component {    constructor(props) {      super(props);      this.state = {        name: 'Mouse'      }    }    render() {      return(              );        }  }}const withCat = (WrappedComponent) => {  return class withCat extends Component {    render() {      return(               )    }  }}const NameComponent = ({name}) => {    return(    
{name}
)}const App =() => { const EnhancedComponent = withMouse(withCat(NameComponent)); return(
)}export default App;

withMousewithCat 都在尝试推送自己的 name 版本。如果EnhancedComponent也必须共享一些同名的props怎么办?


这不会给最终开发人员带来混乱和误导吗? React Devtools 不会报告任何名称冲突,您必须查看 HOC 实现细节才能了解出了什么问题。

这可以通过提供 HOC 属性名称的范围作为约定来解决。因此,您将拥有 withCat_namewithMouse_name 而不是通用的 prop 名称。

这里需要注意的另一件有趣的事情是,对属性进行排序在 React 中非常重要。当您多次拥有相同的属性并导致名称冲突时,最后一个声明将始终保留。在上面的例子中,Cat 获胜,因为它被放置在 { ...this.props } 之后。

如果您希望通过其他方式解决名称冲突,您可以重新排序属性并在最后传播 this.props 。这样,您就可以设置适合您的项目的合理默认值。

使用有意义的显示名称使调试更容易

由 HOC 创建的组件在 React Devtools 中显示为普通组件。很难区分两者。您可以通过为高阶组件提供有意义的 displayName 来简化调试。在 React Devtools 上拥有这样的东西不是明智的吗?

 ... 

那么 displayName 是什么?每个组件都有一个 displayName 属性,可用于调试目的。最流行的技术是包装 WrappedComponent 的显示名称。如果 withCat 是 HOC,并且 NameComponentWrappedComponent,则 displayName 将是 withCat(NameComponent).

const withMouse = (WrappedComponent) => {  class withMouse extends Component {    /*                       */    }  withMouse.displayName = `withMouse(${getDisplayName(WrappedComponent)})`;  return withMouse;}const withCat = (WrappedComponent) => {  class withCat extends Component {   /*                          */  }  withCat.displayName = `withCat(${getDisplayName(WrappedComponent)})`;  return withCat;}function getDisplayName(WrappedComponent) {  return WrappedComponent.displayName || WrappedComponent.name || 'Component';}

高阶组件的替代方案

尽管 Mixins 已经消失,但说高阶组件是唯一允许代码共享和抽象的模式是有误导性的。另一种替代模式已经出现,我听说有人说它比 HOC 更好。深入探讨这个概念超出了本教程的范围,但我将向您介绍渲染道具和一些基本示例,以演示它们为何有用。

渲染道具有许多不同的名称:

渲染道具儿童道具像孩子一样发挥作用渲染回调

这是一个简单的示例,应该解释渲染道具的工作原理。

class Mouse extends Component {  constructor() {    super();    this.state = {      name: "Nibbles"    }  }  render() {    return(      
{this.props.children(this.state)}
) }}class App extends Component { render() { return( {(mouse) =>
The name of the mouse is {mouse.name}
} ) }}

如您所见,我们已经摆脱了高阶函数。我们有一个名为 Mouse 的常规组件。我们将渲染 this.props.children() 并将状态作为参数传递,而不是在其 render 方法中渲染包装的组件。所以我们给 Mouse 一个 render prop,而 render prop 决定应该渲染什么。

换句话说,Mouse 组件接受一个函数作为子属性的值。当 Mouse 渲染时,它返回 Mouse 的状态,并且 render prop 函数可以随意使用它。

我喜欢这种模式的几点:

从可读性的角度来看,道具的来源更加明显。这种模式是动态且灵活的。 HOC 是在静态时间组成的。尽管我从未发现这是一个限制,但渲染道具是动态组合的并且更加灵活。 简化的组件组成。您可以告别嵌套多个 HOC。

结论

高阶组件是可用于在 React 中构建健壮、可重用组件的模式。如果您要使用 HOC,则应遵循一些基本规则。这是为了让您以后不会后悔使用它们的决定。我在本教程中总结了大部分最佳实践。

HOC 并不是当今唯一流行的模式。在本教程的最后,我向您介绍了另一种称为渲染道具的模式,该模式在 React 开发人员中越来越受欢迎。

我不会判断一种模式并说这个模式比另一个模式更好。随着 React 的发展以及它周围的生态系统的成熟,越来越多的模式将会出现。在我看来,您应该学习所有这些,并坚持选择适合您的风格并且您感到舒服的一个。

这也标志着高阶组件教程系列的结束。我们已经从零开始,掌握了一种称为 HOC 的先进技术。如果我错过了任何内容或者您有建议/想法,我很乐意听到。您可以将它们发布在评论中。

以上就是探索在 React 中引入高阶组件的最佳实践的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/535211.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
怎么使用Linux命令移动/复制文件/目录到指定目录下
上一篇 2025年11月9日 12:32:01
如何解决在线交易欺诈问题?MaxMindminFraud与Composer助你构建安全防线
下一篇 2025年11月9日 12:32:02

相关推荐

  • 如何使用正则表达式完整匹配HTML中Script标签的中间内容?

    完整匹配Script标签中间内容的正则表达式 正则表达式是用于从文本中查找特定模式的高级工具。对于HTML中Script标签中间内容的匹配,需要一个特定的正则表达式来实现完整的匹配。 匹配表达式 /(<scriptb([^”]+|”[^”]*”)*>)([sS]*?)()/g 立即学习“…

    2025年12月24日
    3300
  • CSS mask属性无法获取图片:为什么我的图片不见了?

    CSS mask属性无法获取图片 在使用CSS mask属性时,可能会遇到无法获取指定照片的情况。这个问题通常表现为: 网络面板中没有请求图片:尽管CSS代码中指定了图片地址,但网络面板中却找不到图片的请求记录。 问题原因: 此问题的可能原因是浏览器的兼容性问题。某些较旧版本的浏览器可能不支持CSS…

    2025年12月24日
    1210
  • 如何用dom2img解决网页打印样式不显示的问题?

    用dom2img解决网页打印样式不显示的问题 想将网页以所见即打印的的效果呈现,需要采取一些措施,特别是在使用了bootstrap等大量采用外部css样式的框架时。 问题根源 在常规打印操作中,浏览器通常会忽略css样式等非必要的页面元素,导致打印出的结果与网页显示效果不一致。这是因为打印机制只识别…

    2025年12月24日
    1200
  • 如何用 CSS 模拟不影响其他元素的链接移入效果?

    如何模拟 css 中链接的移入效果 在 css 中,模拟移入到指定链接的效果尤为复杂,因为链接的移入效果不影响其他元素。要实现这种效果,最简单的方法是利用放大,例如使用 scale 或 transform 元素的 scale 属性。下面提供两种方法: scale 属性: .goods-item:ho…

    2025年12月24日
    1000
  • 如何调整Flexbox布局中项目对齐方式?

    正文: 调整弹性盒子(Flexbox)布局中项目的对齐方式有几个方法: 文本对齐问题 对于第一个问题,即文字不在 中的问题,这是因为设置了 height 属性。Flexbox 子元素的高度被拉伸到了 height 规定的大小,因此文字无法正常显示在内容内。解决方案是移除 height 属性,让子元素…

    2025年12月24日
    600
  • 如何利用BFC和inline-block解决兄弟元素间margin塌陷问题?

    BFC清除兄弟元素间margin塌陷原理 margin塌陷问题 当相邻的块级元素垂直排列,它们的margin可能会塌陷并重叠,称为margin塌陷。 BFC清除margin塌陷 清除margin塌陷的一种常见方法是将下方元素包裹在一个新的块级格式化上下文(BFC)中,因为BFC之间不会相互影响。 d…

    2025年12月24日
    900
  • Uniapp 中如何不拉伸不裁剪地展示图片?

    灵活展示图片:如何不拉伸不裁剪 在界面设计中,常常需要以原尺寸展示用户上传的图片。本文将介绍一种在 uniapp 框架中实现该功能的简单方法。 对于不同尺寸的图片,可以采用以下处理方式: 极端宽高比:撑满屏幕宽度或高度,再等比缩放居中。非极端宽高比:居中显示,若能撑满则撑满。 然而,如果需要不拉伸不…

    2025年12月24日
    600
  • PC端H5项目如何实现适配:流式布局、响应式设计和两套样式?

    PC端的适配方案及PC与H5兼顾的实现方案探讨 在开发H5项目时,常用的屏幕适配方案是postcss-pxtorem或postcss-px-to-viewport,通常基于iPhone 6标准作为设计稿。但对于PC端网项目,处理不同屏幕大小需要其他方案。 PC端屏幕适配方案 PC端屏幕适配一般采用流…

    2025年12月24日
    800
  • CSS 元素设置 10em 和 transition 后为何没有放大效果?

    CSS 元素设置 10em 和 transition 后为何无放大效果? 你尝试设置了一个 .box 类,其中包含字体大小为 10em 和过渡持续时间为 2 秒的文本。当你载入到页面时,它没有像 YouTube 视频中那样产生放大效果。 原因可能在于你将 CSS 直接写在页面中 在你的代码示例中,C…

    2025年12月24日
    600
  • 如何实现类似横向U型步骤条的组件?

    横向U型步骤条寻求替代品 希望找到类似横向U型步骤条的组件或 CSS 实现。 潜在解决方案 根据给出的参考图片,类似的组件有: 图片所示组件:图片提供了组件的外观,但没有提供具体的实现方式。参考链接:提供的链接指向了 SegmentFault 上的另一个问题,其中可能包含相关的讨论或解决方案建议。 …

    2025年12月24日
    1000
  • 如何让小说网站控制台显示乱码,同时网页内容正常显示?

    如何在不影响用户界面的情况下实现控制台乱码? 当在小说网站上下载小说时,大家可能会遇到一个问题:网站上的文本在网页内正常显示,但是在控制台中却是乱码。如何实现此类操作,从而在不影响用户界面(UI)的情况下保持控制台乱码呢? 答案在于使用自定义字体。网站可以通过在服务器端配置自定义字体,并通过在客户端…

    2025年12月24日
    1100
  • 如何优化CSS Grid布局中子元素排列和宽度问题?

    css grid布局中的优化问题 在使用css grid布局时可能会遇到以下问题: 问题1:无法控制box1中li的布局 box1设置了grid-template-columns: repeat(auto-fill, 20%),这意味着容器将自动填充尽可能多的20%宽度的列。当li数量大于5时,它们…

    2025年12月24日
    1200
  • SASS 中的 Mixins

    mixin 是 css 预处理器提供的工具,虽然它们不是可以被理解的函数,但它们的主要用途是重用代码。 不止一次,我们需要创建多个类来执行相同的操作,但更改单个值,例如字体大小的多个类。 .fs-10 { font-size: 10px;}.fs-20 { font-size: 20px;}.fs-…

    2025年12月24日
    400
  • 绝对定位元素在不同分辨率下偏移,如何解决?

    盒子里的绝对定位元素偏移问题及解决方法 在自定义的输入框checkbox中,对于不同的分辨率设置的居中样式会发生意外的像素偏移,影响选中状态下小红点的居中效果。 偏移的原因在于使用像素单位px。不同分辨率下,像素点的显示方式不同,导致视觉上的错位。 解决方法是将像素单位替换为相对单位,如rem或em…

    2025年12月24日
    400
  • 如何在地图上轻松创建气泡信息框?

    地图上气泡信息框的巧妙生成 地图上气泡信息框是一种常用的交互功能,它简便易用,能够为用户提供额外信息。本文将探讨如何借助地图库的功能轻松创建这一功能。 利用地图库的原生功能 大多数地图库,如高德地图,都提供了现成的信息窗体和右键菜单功能。这些功能可以通过以下途径实现: 高德地图 JS API 参考文…

    2025年12月24日
    1200
  • 如何使用地图库制作悬浮信息框和右键菜单?

    使用地图库制作悬浮信息框和右键菜单的地图显示 想要创建交互式的带有悬浮信息框和右键菜单的地图显示,使用地图库是一个便捷的方法。一般的地图库都提供对应的功能,让你轻松实现这些特性。 功能使用 以高德地图为例,在使用它的 JS API 1.4 时,可以通过以下方式添加信息窗体和右键菜单: 信息窗体:使用…

    2025年12月24日
    100
  • 如何使用 scroll-behavior 属性实现元素scrollLeft变化时的平滑动画?

    如何实现元素scrollleft变化时的平滑动画效果? 在许多网页应用中,滚动容器的水平滚动条(scrollleft)需要频繁使用。为了让滚动动作更加自然,你希望给scrollleft的变化添加动画效果。 解决方案:scroll-behavior 属性 要实现scrollleft变化时的平滑动画效果…

    2025年12月24日
    000
  • CSS mask 属性无法加载图片:浏览器问题还是代码错误?

    CSS mask 属性请求图片失败 在使用 CSS mask 属性时,您遇到了一个问题,即图片没有被请求获取。这可能是由于以下原因: 浏览器问题:某些浏览器可能在处理 mask 属性时存在 bug。尝试更新到浏览器的最新版本。代码示例中的其他信息:您提供的代码示例中还包含其他 HTML 和 CSS …

    2025年12月24日
    000
  • 如何为滚动元素添加平滑过渡,使滚动条滑动时更自然流畅?

    给滚动元素平滑过渡 如何在滚动条属性(scrollleft)发生改变时为元素添加平滑的过渡效果? 解决方案:scroll-behavior 属性 为滚动容器设置 scroll-behavior 属性可以实现平滑滚动。 html 代码: click the button to slide right!…

    2025年12月24日
    1100
  • 如何解决用户代理样式表导致页面元素无法显示的问题?

    去除用户代理样式表的样式 在项目中遇到用户代理样式表的样式遮盖了页面元素,导致无法显示的情况,这可能是因为安装了去广告插件导致的。 通常,用户代理样式表是在浏览器中预定义的,用于提供默认的样式。然而,在特殊情况下,某些插件或扩展程序可能会注入自己的用户代理样式表,从而覆盖页面上的现有样式。 在这种情…

    2025年12月24日
    000

发表回复

登录后才能评论
关注微信