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

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

相关推荐
To_OC12 小时前
踩了个 TS 的坑之后,我终于把 type 和 interface 掰明白了
前端·react.js·typescript
Eloudy18 小时前
ReAct 原理简介
前端·javascript·人工智能·react.js·agent·gpu
嘟嘟071720 小时前
Next.js 博客左侧笔记列表是怎么从 Redis 渲染出来的?从组件、children 到动态路由一次讲清
redis·react.js·前端工程化
张元清21 小时前
React useInterval Hook:没有过期闭包的 setInterval (2026)
javascript·react.js
MXN_小南学前端21 小时前
React超长文本域中实现“返回顶部”浮动按钮
前端·javascript·react.js
YIAN1 天前
http无状态?State来展示!从基础路由到鉴权守卫,吃透 SPA 前端路由核心
前端·react.js
console.log('npc')1 天前
React 19 + Vite 企业级前端项目:从零搭建到规范交付
前端·react.js·前端框架
光影少年1 天前
react离线缓存、图片缓存方案
开发语言·前端·javascript·react native·react.js·缓存·前端框架
抱抱宝2 天前
Agent-study项目教程(03):手写 Mini-ReAct Agent(不依赖框架)
javascript·人工智能·gpt·react.js·prompt·agent
想要成为糕糕手2 天前
🚀 在浏览器里跑 DeepSeek-R1?WebGPU 端侧推理实战(六)—— 完结篇:消息渲染与流式收尾
react.js·typescript·llm