如何编写难以维护的 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>
  )
}

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

相关推荐
因为奋斗超太帅啦19 分钟前
React学习笔记(三)——React 组件通讯
笔记·学习·react.js
西瓜本瓜@3 小时前
React + React Image支持图像的各种转换,如圆形、模糊等效果吗?
前端·react.js·前端框架
黄毛火烧雪下3 小时前
React 的 useEffect 钩子,执行一些异步操作来加载基本信息
前端·chrome·react.js
蓝莓味柯基3 小时前
React——点击事件函数调用问题
前端·javascript·react.js
资深前端之路3 小时前
react jsx
前端·react.js·前端框架
白鹭凡7 小时前
react 甘特图之旅
前端·react.js·甘特图
Passion不晚11 小时前
Vue vs React vs Angular 的对比和选择
vue.js·react.js·前端框架·angular.js
光影少年20 小时前
usemeno和usecallback区别及使用场景
react.js
吕彬-前端1 天前
使用vite+react+ts+Ant Design开发后台管理项目(二)
前端·react.js·前端框架
小白小白从不日白1 天前
react hooks--useCallback
前端·react.js·前端框架