当前位置: 首页 > web >正文

React 单一职责原则:优化组件设计与提高可维护性

fileOf7174.png

单一职责原则(SRP)

在 React 中,组件是构建 UI 的核心单位,而良好的组件设计是保证应用质量和可维护性的关键。单一职责原则是一种设计原则,也适用于 React 组件的开发。它强调每个组件应该只关注一个职责,这样可以提高组件的可维护性、复用性以及降低代码的复杂度。

什么是单一职责原则

单一职责原则的定义是每个类应该只有一个职责,   也就是只做一件事。这个原则是最容易解释的,因为我们可以简单地将其理解为“每个功能/模块/组件都应该只做一件事”。

在 React 组件中,单一职责原则要求将组件的功能分解为更小的部分,每个部分只负责一个特定的职责。这样做的好处是,当需求发生变化时,只需要修改与该职责相关的部分,而不会影响整个组件。这提高了代码的可维护性和可测试性。

在所有这些原则中,单一职责原则是最容易遵循的,也是最有影响力的一项,因为它极大提高了代码的质量。为了确保组件只做一件事,可以这样:

  • 将功能较多的大型组件拆分为较小的组件;

  • 将与组件功能无关的代码提取到单独的函数中;

  • 将有联系的功能提取到自定义 Hooks 中。

如何应用单一职责原则在 React 中

在 React 中应用单一职责原则主要通过组件的拆分和组合来实现。以下是一些建议:

  1. 拆分大型组件:当一个组件变得庞大且复杂时,考虑将其拆分为更小的组件,每个组件关注不同的职责。这样可以提高可维护性和复用性。

  2. 提取可复用的功能:识别重复使用的功能,将其提取为单独的组件,并通过 props 进行参数化。这样可以减少重复代码,提高复用性。

  3. 保持组件的简洁和独立性:确保每个组件只关注自己的职责,并且不包含与其他职责无关的代码。这样可以降低代码的复杂度和维护成本。

  4. 使用高阶组件(Higher-Order Components):高阶组件是一种函数,接受一个组件作为参数,并返回一个新的增强组件。通过使用高阶组件,可以将与特定职责相关的逻辑与组件分离,提高代码的可维护性。

  5. 使用自定义钩子(Custom Hooks):自定义钩子是一种将组件逻辑进行复用的方式。通过将与特定职责相关的逻辑封装在自定义钩子中,可以使组件更加简洁和独立。

下面来看一个显示活跃用户列表的组件:

const ActiveUsersList = () => {const [users, setUsers] = useState([])useEffect(() => {const loadUsers = async () => {  const response = await fetch('/some-api')const data = await response.json()setUsers(data)}loadUsers()}, [])const weekAgo = new Date();weekAgo.setDate(weekAgo.getDate() - 7);return (<ul>{users.filter(user => !user.isBanned && user.lastActivityAt >= weekAgo).map(user => <li key={user.id}><img src={user.avatarUrl} /><p>{user.fullName}</p><small>{user.role}</small></li>)}</ul>    )
}

这个组件虽然代码不多,但是做了很多事情:获取数据、过滤数据、渲染数据。来看看如何分解它。

首先,只要同时使用了  useState  和  useEffect,就可以将它们提取到自定义 Hook 中:

const useUsers = () => {const [users, setUsers] = useState([])useEffect(() => {const loadUsers = async () => {  const response = await fetch('/some-api')const data = await response.json()setUsers(data)}loadUsers()}, [])return { users }
}const ActiveUsersList = () => {const { users } = useUsers()const weekAgo = new Date()weekAgo.setDate(weekAgo.getDate() - 7)return (<ul>{users.filter(user => !user.isBanned && user.lastActivityAt >= weekAgo).map(user => <li key={user.id}><img src={user.avatarUrl} /><p>{user.fullName}</p><small>{user.role}</small></li>)}</ul>    )
}

现在,useUsers Hook 只关心一件事——从 API 获取用户。它使我们的组件代码更具可读性。

接下来看一下组件渲染的 JSX。每当我们对对象数组进行遍历时,都应该注意它为每个数组项生成的 JSX 的复杂性。如果它是一个没有附加任何事件处理函数的单行代码,将其保持内联是完全没有问题的。但对于更复杂的 JSX,将其提取到单独的组件中可能是一个更好的主意:

const UserItem = ({ user }) => {return (<li><img src={user.avatarUrl} /><p>{user.fullName}</p><small>{user.role}</small></li>)
}const ActiveUsersList = () => {const { users } = useUsers()const weekAgo = new Date()weekAgo.setDate(weekAgo.getDate() - 7)return (<ul>{users.filter(user => !user.isBanned && user.lastActivityAt >= weekAgo).map(user => <UserItem key={user.id} user={user} />)}</ul>    )
}

这里将用于呈现用户信息的逻辑提取到了一个单独的组件中,从而使我们的组件更小、更可读。

最后,从 API 获取到的用户列表中过滤出所有非活跃用户的逻辑是相对独立的,可以在其他部分重用,所以可以将其提取到一个公共函数中:

const getOnlyActive = (users) => {const weekAgo = new Date()weekAgo.setDate(weekAgo.getDate() - 7)return users.filter(user => !user.isBanned && user.lastActivityAt >= weekAgo)
}const ActiveUsersList = () => {const { users } = useUsers()return (<ul>{getOnlyActive(users).map(user => <UserItem key={user.id} user={user} />)}</ul>    )
}

到现在为止,通过上面三步拆解,组件已经变得比较简单。但是,仔细观察会发现,这个组件还有优化的空间。目前,组件首先获取数据,然后需要对数据进行过滤。理想情况下,我们只想获取数据并渲染它,而不需要任何额外的操作。所以,可以将这个逻辑封装到一个新的自定义 Hook 中,最终的代码如下:

// 获取数据
const useUsers = () => {const [users, setUsers] = useState([])useEffect(() => {const loadUsers = async () => {  const response = await fetch('/some-api')const data = await response.json()setUsers(data)}loadUsers()}, [])return { users }
}// 列表渲染
const UserItem = ({ user }) => {return (<li><img src={user.avatarUrl} /><p>{user.fullName}</p><small>{user.role}</small></li>)
}// 列表过滤
const getOnlyActive = (users) => {const weekAgo = new Date()weekAgo.setDate(weekAgo.getDate() - 7)return users.filter(user => !user.isBanned && user.lastActivityAt >= weekAgo)
}const useActiveUsers = () => {const { users } = useUsers()const activeUsers = useMemo(() => {return getOnlyActive(users)}, [users])return { activeUsers }
}const ActiveUsersList = () => {const { activeUsers } = useActiveUsers()return (<ul>{activeUsers.map(user => <UserItem key={user.id} user={user} />)}</ul>    )
}

在这里,我们创建了useActiveUsers Hook 来处理获取和过滤数据的逻辑,而组件只做了最少的事情——渲染它从 Hook 中获取的数据。

现在,这个组件只剩下两个职责:获取数据渲染数据,当然我们也可以在组件的父级获取数据,并通过 props 传入该组件,这样只需要渲染组件就可以了。当然,还是要视情况而定。我们可以简单地将获取并渲染数据看作是“一件事”。

总结

使用单一职责原则可以获得以下好处:

  1. 提高可维护性:将组件分解为小的职责模块,使其更易于理解和修改。当需要更新或修复特定功能时,可以更快地定位和处理相关代码。

  2. 提高复用性:通过将职责分离为独立的模块,可以更好地重用组件的不同部分。这样可以减少重复代码,提高开发效率。

  3. 降低代码耦合:职责分离使得组件之间的依赖关系更清晰。当一个组件只关注一个职责时,它变得更加独立,减少了与其他组件的耦合性。

通过应用单一职责原则,我们可以优化 React 组件的设计,提高其可维护性和复用性。将组件分解为小的职责模块,提取可复用的功能,保持组件的简洁和独立性,使用高阶组件和自定义钩子等技术,都是实现单一职责原则的有效方法。

总而言之,遵循单一职责原则,我们有效地采用了大量独立的代码并使其更加模块化,模块化的代码更容易测试和维护。

http://www.xdnf.cn/news/1063.html

相关文章:

  • 马浩棋:产通链CT-Chain 破局不动产 RWA,引领数智金融新变革
  • 程序生成随机数
  • 什么是API
  • 数智读书笔记系列030《曲折的职业道路:在终身工作时代找准定位》与《做自己的教练:战胜工作挑战掌控职业生涯》
  • 离线-DataX
  • 【AI微信小程序开发】大转盘小程序项目代码:自设转盘选项和概率(含完整前端+后端代码)
  • 刷题之路:C++ 解题分享与技术总结
  • Yocto项目实战教程-第7章定制镜像菜谱与内核菜谱-7.2小节-定制应用程序
  • 眼镜眨巴眨巴-一步几个脚印从头设计数字生命2——仙盟创梦IDE
  • OpenHarmony OS 5.0与Android 13显示框架对比
  • 【python】如何将文件夹及其子文件夹下的所有word文件汇总导出到一个excel文件里?
  • 操作系统-用户级-内核级线程
  • 西门子地址表规划中,如WM200与WM201这样相邻地址冲突问题
  • 工厂模式:工厂方法模式 和 抽象工厂模式
  • VulnHub-DarkHole_1靶机渗透教程
  • Linux驱动模块双机调试详细步骤
  • Linux学习——UDP
  • PowerQuery逆透视将二维表转换为一维表
  • 【家政平台开发(67)】家政平台移动端性能优化:打造极致用户体验
  • Spring集合注入Bean
  • Oracle数据库学习之路-目录
  • PyTorch与自然语言处理:从零构建基于LSTM的词性标注器
  • Docker离线安装与配置指南
  • 安装 Conda 环境
  • Linux 一些常用的命令记录
  • pdf多文件合并
  • Elasticsearch性能优化实践
  • SQL简介
  • SystemV-消息队列与责任链模式
  • 神经网络的 “成长密码”:正向传播与反向传播深度解析(四)