在众多项目中,React代码的维护经常变得棘手。其中一个常见问题是:将业务逻辑直接嵌入通用组件中,导致通用组件与业务逻辑紧密耦合,使其失去“通用性”。这种做法使通用组件过于依赖具体业务逻辑,导致代码难以维护和扩展。

示例:屎山是如何逐步堆积的

让我们看一个例子:我们在业务组件 PageA 和 PageB 中都使用了通用组件 Card。

function PageA() {
return (
<>
{/* ... */}
{listA.map(({ title, content }) => <Card title={title} content={content} />)}
</>
)
} function PageB() {
return (
<>
{/* ... */}
{listB.map(({ title, content }) => <Card title={title} content={content} />)}
</>
)
} function Card({ title, content }) {
return (
<div>
<Title>{title}</Title>
<Content>{content}</Content>
</div>
)
}

某一天,出现了一个新需求:在手机端的所有页面都需要显示 Footer。于是,代码被修改如下:

function PageA({ isMobile }) {
return (
<>
{/* ... */}
{listA.map(({ title, content }) => (
<Card title={title} content={content} isMobile={isMobile} />
))}
</>
)
} function PageB({ isMobile }) {
return (
<>
{/* ... */}
{listB.map(({ title, content }) => (
<Card title={title} content={content} isMobile={isMobile} />
))}
</>
)
} function Card({ title, content, isMobile }) {
return (
<div>
<Title>{title}</Title>
<Content>{content}</Content>
{isMobile && <Footer />}
</div>
)
}

随后的某一天,小张接手了这个项目,又有新需求:只有第偶数个 Card 才应该显示 Footer。于是,秉承着最小影响范围的原则,代码被改成了这样:

function PageA({ isMobile }) {
return (
<>
{/* ... */}
{listA.map(({ title, content }, index) => (
<Card title={title} content={content} isMobile={isMobile} index={index} />)
)}
</>
)
} function PageB({ isMobile }) {
return (
<>
{/* ... */}
{listB.map(({ title, content }, index) => (
<Card title={title} content={content} isMobile={isMobile} index={index} />)
)}
</>
)
} function Card({ title, content, isMobile, index }) {
return (
<div>
<Title>{title}</Title>
<Content>{content}</Content>
{isMobile && index % 2 === 1 && <Footer />}
</div>
)
}

随后的某一天,小王接手了这个项目,又有新需求。秉承着最小影响范围的原则 ......

分析原因

乍看之下,每次修改都是“局部最优”的,尽量修改最少的代码以限制影响范围,以确保在添加新功能时不引入错误。然而,实际上,由于每次“偷懒”,我们都违反了原则,导致代码变得越来越混乱。

原则

分离关注点原则(Separation of Concerns)是计算机科学和软件工程的基本设计原则之一,旨在帮助程序员更好地组织和管理复杂的系统。该原则的核心思想是将大型系统或程序分解为多个互相独立的组件,每个组件负责解决特定的关注点或任务,而不会受到其他关注点的干扰。这有助于提高代码的可维护性、可扩展性和可重用性。

重构

将上述原则应用于这个示例中:通用组件应该只了解与自身相关的信息,Card 组件只关心何时显示 Footer,而不关心它在何处使用以及是否为第偶数个。让我们重构代码:

function PageA({ isMobile }) {
return (
<>
{/* ... */}
{listA.map(({ title, content }, index) => (
<Card title={title} content={content} showFooter={isMobile && index % 2 === 1} />)
)}
</>
)
} function PageB({ isMobile }) {
return (
<>
{/* ... */}
{listB.map(({ title, content }, index) => (
<Card title={title} content={content} showFooter={isMobile && index % 2 === 1} />)
)}
</>
)
} function Card({ title, content, showFooter }) {
return (
<div>
<Title>{title}</Title>
<Content>{content}</Content>
{showFooter && <Footer />}
</div>
)
}

通过这次重构,我们成功解耦了通用组件和业务逻辑,使代码更易于维护和扩展。

如何编写难以维护的 React 代码?耦合通用组件与业务逻辑的更多相关文章

  1. 编写可维护的JavaScript代码(部分)

    平时使用的时VS来进行代码的书写,VS会自动的将代码格式化,所有写了这么久的JS代码,也没有注意到这些点.看了<编写可维护的javascript代码>之后,做了些笔记. var resul ...

  2. 一步步教你编写不可维护的 PHP 代码

    译者注:这是一篇很棒文章,使用有趣的叙述方式,从反面讲解了作为一个优秀的 PHP 工程师,有哪些事情是你不能做的.请注意哦,此篇文章罗列的行为,都是你要尽量避免的. 随着失业率越来越高,很多人意识到保 ...

  3. 编写可维护的js代码

    在工作中,制定一套统一的编码风格,可以提高开发效率,维护起来的也要容易很多,也能避免一些不必要的错误出现. 项目中常用的检查编码风格的工具JSLint.JSHint.JSCS.ESLint,,在这呢, ...

  4. 编写可维护的javascript代码--- 2015.11.22(注释)

    1.单行注释 // 这是一句单行注释 2.多行注释 /* 这里是代码 */     /*  这里都是注释 1232132  */      java的注释风格 /* * 另一段注释 * 这段注释包含2 ...

  5. 编写可维护的javascript代码--- 2015.11.21(基本格式化)

    1.1 每行的编码需要控制在80字符. 1.2 改用:的地方必须用上. 1.3 缩进用2个制表符,不过4个也可以. 1.4 当代码一行显示不全需要折行显示,这里我暂且假定缩进为4个字符. 1.5 如果 ...

  6. 编写可维护的javascript代码---开篇(介绍自动报错的插件)

    文章开篇主要推荐了2款检测编程风格的工具: JSLint和JSHint: jsLint是由Douglas Crockford创建的.这是一个通用的javascript代码质量检测工具,最开始JSLin ...

  7. 编写可维护的JavaScript代码

    1.  1)for-in循环用来遍历对象属性.不仅遍历对象的实例属性,还遍历从原型继承来的属性. 所以最好使用hasOwnProperty()方法来过滤. 2) for in循环遍历出的对象属性不能保 ...

  8. 翻译 | 玩转 React 表单 —— 受控组件详解

    原文地址:React.js Forms: Controlled Components 原文作者:Loren Stewart 译者:小 B0Y 校对者:珂珂君 本文涵盖以下受控组件: 文本输入框 数字输 ...

  9. 《代码不朽:编写可维护软件的10大要则(C#版)》读后感

    本书作者Joost Visser,译者张若飞.本书讲解了编写可维护代码的10个要则,从目录就可以看出这10点分别是: 编写短小的代码单元(15行以内,在大部分情况下还是能实现的,但是当我们使用Linq ...

  10. 如何编写可维护的面向对象JavaScript代码

    能够写出可维护的面向对象JavaScript代 码不仅可以节约金钱,还能让你很受欢迎.不信?有可能你自己或者其他什么人有一天会回来重用你的代码.如果能尽量让这个经历不那么痛苦,就可以节省不少时 间.地 ...

随机推荐

  1. JDBC详解(韩顺平教程)

    JDBC 一.原理示意图 二.前提步骤 IDEA导入MySQL的jdbc驱动,并操作数据库 - 打点 - 博客园 (cnblogs.com) 三.JDBC编写步骤: 用法1: package Hsp. ...

  2. PlayWright(十二)- PO模式

    1.PO模式是什么? PO,即Page Object,直译为页面对象,代表 Web 应用程序的一部分   具体什么意思呢,通俗来讲,一个页面有输入.点击.搜索功能,而且有很多页面,这时候我们就采用每个 ...

  3. .NET表达式树

    IQueryable/IQueryable 和表达式树 IQueryable有两个组件 Expression:当前查询的组件的与语言和数据源无关的表示形式,以表达式树的形式表示. Provider:L ...

  4. 效率回归,工具库之美「GitHub 热点速览」

    刚开源就变成新星的 igl,不仅获得了 2k+ star,也能提高你开发游戏的效率,摆平一切和图形有关的问题.如果这个没有那么惊艳的话,还有 The-Art-of-Linear-Algebra,重燃了 ...

  5. GoFrame v2.5 版本发布,企业级 Golang 开发框架

    大家好啊,GoFrame 框架今天发布了 v2.5.0 正式版本啦! 本次版本主要是对已有功能组件以及开发工具上的改进工作.其中,开发工具新增了 gf gen ctrl 命令,以规范化定义.开发 AP ...

  6. Chrome 报错: Unchecked runtime.lastError: Could not establish connection. Receiving end does not exist.

    经检查,是由浏览器中的插件导致的报错. 解决方案: 将该插件移除或关闭

  7. linux下创建虚拟环境

    安装虚拟环境: 1 sudo apt-get install virtualenvwrapper 配置环境变量: 1.创建目录用于存放虚拟环境. 1 mkdir $HOME/.virtualenvs ...

  8. Command "python setup.py egg_info" failed with error code 1 in /tmp/pip-install-4_w0c665/PyQt5/

    错误: 解决方式:输入一下命令 1 pip3 install --upgrade setuptools 2 python3 -m pip install --upgrade pip 输入命令: 1 p ...

  9. Scala的基本使用

    @ 目录 Scala的基本使用 一.基础语法 1.1 变量 1.1.1 var和val 1.1.2 自动类型推断 1.2 数据类型 1.2.1 基础数据类型 1.2.2 增强数据类型 1.3 操作符 ...

  10. Tcpdump 使用指南

    论网络数据包的分析,我首选wireshark,因为图形化界面直观明了.但如果遇到没有图形化显示的Linux环境,那么此时会使用tcpdump该是一件多么美好的事情. 网上关于tcpdump的介绍很多, ...