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

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

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

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

jsx 复制代码
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。于是,代码被修改如下:

jsx 复制代码
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。于是,秉承着最小影响范围的原则,代码被改成了这样:

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

开放-封闭原则(Open-Closed Principle,OCP):

这个原则表明软件实体(类、模块、函数等)应该对扩展开放,但对修改关闭。这意味着应该通过扩展现有的代码来引入新功能,而不是修改已有的代码。这有助于减少代码的风险,因为修改现有代码可能导致不可预测的副作用。

重构

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

jsx 复制代码
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>
  )
}

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

相关推荐
凯哥爱吃皮皮虾7 小时前
如何给 react 组件写单测
前端·react.js·jest
每一天,每一步10 小时前
react antd点击table单元格文字下载指定的excel路径
前端·react.js·excel
screct_demo20 小时前
詳細講一下在RN(ReactNative)中,6個比較常用的組件以及詳細的用法
javascript·react native·react.js
光头程序员1 天前
grid 布局react组件可以循数据自定义渲染某个数据 ,或插入某些数据在某个索引下
javascript·react.js·ecmascript
limit for me1 天前
react上增加错误边界 当存在错误时 不会显示白屏
前端·react.js·前端框架
浏览器爱好者1 天前
如何构建一个简单的React应用?
前端·react.js·前端框架
VillanelleS1 天前
React进阶之高阶组件HOC、react hooks、自定义hooks
前端·react.js·前端框架
某哈压力大1 天前
基于react-vant实现弹窗搜索功能
前端·react.js
傻小胖1 天前
React 中hooks之useInsertionEffect用法总结
前端·javascript·react.js
flying robot2 天前
React的响应式
前端·javascript·react.js