写点什么

重构复杂的 React 组件:编写高效且可读组件的 5 个最佳实践

  • 2019-10-30
  • 本文字数:4972 字

    阅读完需:约 16 分钟

重构复杂的React组件:编写高效且可读组件的5个最佳实践

随着 React.js 的不断进化,现在的它已经成为 Web 组件中最受欢迎的视图库之一。但是你手中的它,是否真的能够正常工作呢?本文将主要描述 5 个关于 React 组件的最佳实践,希望对正在关注 React 组件的你有所帮助。

问题

React.js 已成为 Web 组件中最受欢迎的视图库。一路进化下来,它发展出了众多特性,如今已成为创建优秀的 Web 应用程序的一套完整工具。


它的社区经历了爆发式增长,尤其在过去的 2-3 年中,网络上出现了成千上万有关这项技术的教程。


因此,每位初学者在开始学习 React 时都应该做一件事情,那就是阅读其文档或教程进而创建他们的第一个组件,就像我在 Codeworks 上开始我的学习旅途一样。


但我的问题是:你能肯定你的 React 组件遵循了最佳实践吗?简单来说,它们是不是正常工作呢?

脏组件长什么样

为了更好地说明我的观点,让我们来看看下面的 React 组件:


import React from 'react';import './Listcomponent.css';
class Listcomponent extends React.Component { constructor(props) { super(props);
this.state = { lastClickedButton: '' }; }
render() { return ( <div> <hl>The last clicked button is {this.state.lastClickedButton}</hl> <ul> <li> <button onClick={() => { this.setState({ lastClickedButton: 'Create' }); this.props.createSomething(); }} className="my-button"> Create </button> </li> <li> <button onClick={() => { this.setState({ lastClickedButton: 'Read' }); this.props.readSomething(); }} className="my-button"> Read </button> </li> <li> <button onClick={() => { this.setState({ lastClickedButton: 'Update' }); this.props.updateSomething(); }} className="my-button"> Update </button> </li> <li> <button onClick= {() => { this.setState({ lastClickedButton: 'Destroy' }); this.props.destroySomething(); }} className="my-button"> Destroy </button> </li> </ul> </div> ); }}
复制代码


一个肮脏的 React 组件


这是一个完全正常工作的 React 组件,可以在整个应用程序中多次使用。它渲染了一个按钮列表,这些按钮会触发某个事件,组件还会显示最近被点击的是哪个按钮。总之很简单。


你可能会想:“好吧……如果能用,那就没什么问题!”


但如果有人告诉你,现在这个用 62 行代码写成的组件其实用少得多的代码也能做出来呢?所以我们开始做扫除吧!

1. 优先使用 React Hooks 实现函数组件

随着 React 16.8 引入 Hooks,我们就可以在类声明中使用函数组件来构成有状态组件(如果我们需要处理任何逻辑)了。


在本文中,我们不会深入讨论类与函数组件或 React Hooks。但在 React 社区中众所周知的是,最好优先创建函数组件,尤其是现在我们有了 Hooks 这么好用的工具。


Hooks 允许你复用状态逻辑,而无需更改组件层次结构。


接下来让我们看一下第一次重构后组件的样子:


import React, { useState } from 'react';import './ListComponent.css';const ListComponent = props => {  const [lastClickedButton, setLastClickedButton] = useState('');
return ( <div> <hl>The last clicked button is {lastClickedButton}</hl> <ul> <li> <button onClick={() => { setLastClickedButton('Create'); props.createSomething(); }} className="my-button"> Create </button> </li> <li> <button onClick={() => { setLastClickedButton('Read'); props.ReadSomething(); }} className="my-button"> Read </button> </li> <li> <button onClick={() => { setLastClickedButton('Update'); props.updateSomething(); }} className="my-button"> Update </button> </li> <li> <button onclick={() => { setLastClickedButton('Destroy'); props.DestroySomething(); }} className="my-button"> Destroy </button> </li> </ul> </div> );};
复制代码


用 React Hooks 重构成函数组件很好,我们的组件已经短一些了,我们还删除了 类 语法,但仍然需要做许多优化工作。

2. 利用好它!

我们可以在这个组件中找到什么模式吗?看一下代码,似乎我们每次都渲染一个相似的 button 元素,每个元素都接受一些相似的 props,所以非常适合把这个长组件切成许多小块。


因此我们可以进一步重构这个组件,创建另一个小的函数组件来渲染按钮,并传递一些属性,如 action、setClicked 和 title:


import React, { useState } from 'react';import './ListComponent.css';const ListItemComponent = props => {  return {    <li>      <button        onClick={() => {          props.setClicked(props.title);          props.action();        }}        className="my-button">        {props.title}      </button>    </li>  );};const ListComponent = props => {  const [lastClickedButton, setLastClickedButton] = useState('');  return    <div>      <hl>The last clicked button is {lastClickedButton}</hl>      <ul>        <ListItemcomponent          title="Create"          action={props.createSomething}          setClicked={setLastClickedButton}        />        <ListItemComponent          title="Read"          action={props.readSomething}          setClicked={setLastClickedButton}        />        <ListItemComponent          title="Update"          action={props.updateSomething}          seteClicked={setLastClickedButton}        />        <ListItemComponent          title="Destroy"          action={props.destroySomething}          seteClicked={setLastClickedButton}        />      </ul>    </div>  );};
复制代码


好的,我们的组件开始变好看了,但是仍有改进的余地,让我们继续吧!

3. 正确命名和 props 解构

setLastClickedButton 是 setter 函数的描述性名称,但我们需要保持代码的可读性和简洁,因此请务必起一个最短、最精炼的名字,这是很重要的。我们将其重命名为 setClicked。


同样,只要有可能,从 props 对象解构出来你需要的东西就可以避免多次重复使用 props 这个词。在 ListItem 组件中,我们现在按解构后的函数参数中的名称—— {action, title, setClicked}来访问 props。


下面看看这两个变化:


import React, { useState } from 'react';import './List.css';const ListItem = ({ action, title, setClicked }) => {  return {    <li>      <button        onClick={() => {          setclicked(title);          action();        }}        className="my-button">        {title}      </button>    </li>  );};const List = ({ create, read, update, destroy }) => {  const [clicked, setClicked] = useState('');  return (    <div>      <hl>The last clicked button is {clicked}</hl>      <ul>        <ListItem title="Create" action={create} setClicked={setClicked} />        <ListItem title="Read" action={read} setClicked={setClicked} />        <ListItem title="Update" action={update} setClicked={setClicked} />        <ListItem title ="Destroy" action={destroy} setClicked={setClicked} />      </ul>    </div>  );};
复制代码


太好了,我们大大减少了组件声明的长度,但是我们仍然可以做得更好!

4. 愿 PropTypes 与你同在!

经过清理之后,该是用到编写组件时最棒的实践的时候了!使用 PropTypes,我们可以验证接收到的 props,以避免由于不同数据类型而导致的错误。例如,接收字符串“0”并尝试将其与数字 0 严格对比(“0” === 0-> FALSE!!!):


import React, { useState } from 'react';import PropTypes from 'prop-types';
const ListItem = ({ action, title, setClicked }) => { return ( <li> <button onClick={() => { setClicked(title); action(); }} className="my-button"> {title} </button> </li> );};ListItem.propTypes = { action: PropTypes.func, setClicked: PropTypes.func, title: PropTypes.string};
const List = ({ create, read, update, destroy }) => { const [clicked, setClicked] = useState('');
return ( <div> <hl>The last clicked button is {clicked}</hl> <ul> <ListItem title="Create" action={create} setClicked={setClicked} /> <ListItem title="Read" action={read} setClicked={setClicked} /> <ListItem title="Update" action={update} setClicked={setClicked} /> <ListItem title ="Destroy" action={destroy} setClicked={setClicked} /> </ul> </div> );};List.propTypes = { create: PropTypes.func, read: PropTypes.func, update: PropTypes.func, destroy: PropTypes.func,};export default List;
复制代码


PropTypes 验证

5. 切成小块

想不到吧——我们现在的组件与初始版本差不多一样长,但请仔细观察我们现在手上的代码。


我们看到了两个不同的组件,可以将它们划分为两个模块,从而使它们在整个应用程序中都能复用。


import React, { useState } from 'react';import PropTypes from 'prop-types';import ListItem from './ListItem.js'
const List = ({ create, read, update, destroy }) => { const [clicked, setClicked] = useState('');
return ( <div> <hl>The last clicked button is {clicked}</hl> <ul> <ListItem title="Create" action={create} setClicked={setClicked} /> <ListItem title="Read" action={read} setClicked={setClicked} /> <ListItem title="Update" action={update} setClicked={setclicked} /> <ListItem title ="Destroy" action={destroy} setClicked={setclicked} /> </ul> </div> );};};

List.propTypes = { create: PropTypes.func, read: PropTypes.func, update: PropTypes.func, destroy: PropTypes.func,};
export default List;
复制代码


List.js


import React, { useState } from 'react';import PropTypes from 'prop-types';
const ListItem = ({ action, title, setClicked }) => { return ( <li> <button onClick={() => { setClicked(title); action(); }} className="my-button"> {title} </button> </li> );};
ListItem.propTypes = { action: PropTypes.func, setClicked: PropTypes.func, title: PropTypes.string};export default ListItem;
复制代码


ListItem.js

小结

当你开始研究 React 组件时,本文对初始组件的这些清理工作提供了一些值得参考的优秀实践。


当然,我们可以针对这个最终结果执行其他很多优化操作,但路要一步一步走,这五个优秀实践是很好的起点。


作者介绍:


Marco Antonio Ghiani。"什么事做起来连两分钟都用不了的话,那就试一下吧。"D.A. Coding JS at @xceed。热爱编程。


原文链接:


https://levelup.gitconnected.com/refactoring-a-complex-react-component-5-best-practices-to-write-efficient-and-readable-components-b0d06f4f22b4


2019-10-30 19:323497
用户头像
王文婧 InfoQ编辑

发布了 126 篇内容, 共 73.6 次阅读, 收获喜欢 275 次。

关注

评论 1 条评论

发布
用户头像
太简单了 ... 感觉没什么帮助
2021-03-11 20:09
回复
没有更多了
发现更多内容

龙博机电:90后“厂二代”,靠伙伴云零代码让中小制造业实现数字化“逆袭”

联营汇聚

涅槃重生!字节大牛力荐大型分布式手册,凤凰架构让你浴火成神

Java你猿哥

架构 Kubernetes 分布式 架构师 分布式架构

40亿个QQ号,限制1G内存,如何去重?

Java你猿哥

Java ssm 布隆过滤器 BitMap 过滤器

阿里巴巴亿级并发系统设计手册已开源(2023 最新版)震撼来袭

架构师之道

Java 高并发

最高奖金100万!第二届广州·琶洲算法大赛火热报名中

飞桨PaddlePaddle

百度飞桨 算法大赛

Velocity不用愁!Velocity系统的前端工程化之路 | 京东云技术团队

京东科技开发者

Java 前端工程化 Web H5 Velocity.js 企业号 5 月 PK 榜

企业研发效能度量利器,华为云发布CodeArts Board看板服务

华为云开发者联盟

云计算 后端 华为云 华为云开发者联盟 企业号 5 月 PK 榜

全新一代小度智能屏X9焕新上市 正式开启预售

Geek_2d6073

2023最新版Java面试八股文大全(附各大厂面试真题及答案)

采菊东篱下

java面试

实例解读华为云数字工厂平台的逻辑模型编排器

华为云开发者联盟

数据库 后端 华为云 华为云开发者联盟 企业号 5 月 PK 榜

又爆神作!阿里首发并发编程神仙笔记,差距不止一点点

做梦都在改BUG

Java 并发编程

浅谈财务共享未来发展趋势

用友BIP

业财融合 财务共享

使用 PAI-Blade 优化 Stable Diffusion 推理流程(二)

阿里云大数据AI技术

人工智能 优化 推理 Stable Diffusion 企业号 5 月 PK 榜

阿里SpringBoot实战手册横空出世!从此不再是易学难精

做梦都在改BUG

Java spring 微服务 Spring Boot 框架

混沌演练实践(二)-支付加挂链路演练 | 京东云技术团队

京东科技开发者

微服务 混沌工程 混沌工程实践 企业号 5 月 PK 榜

直击灵魂!美团大牛手撸并发原理笔记,由浅入深剖析JDK源码

做梦都在改BUG

Java 并发编程 多线程 jdk源码

ps vs top:CPU占用率统计的两种不同方式

极限实验室

Linux 运维 监控系统 INFINI Console

胜面试官半子!阿里SpringBoot全栈笔记首发,源码实战齐飞

做梦都在改BUG

Java spring 微服务 Spring Boot 框架

AntDB数据库参加开源数据库技术沙龙,分享全栈业务能力

亚信AntDB数据库

AntDB AntDB数据库 企业号 5 月 PK 榜

我用ChatGPT,给RabbitMQ加了个连接池

Java你猿哥

Java 源码 ssm RabbitMQ ChatGPT

背完这套Java面试八股文,自动解锁面试牛逼症被动技能

Java你猿哥

MySQL redis java面试 java基础 分布式微服务

Elasticsearch与Clickhouse数据存储对比 | 京东云技术团队

京东科技开发者

数据库 elasticsearch Clickhouse 企业号 5 月 PK 榜

云图说丨初识商标注册服务

华为云开发者联盟

云计算 后端 华为云 华为云开发者联盟 企业号 5 月 PK 榜

百度工程师移动开发避坑指南——Swift语言篇

百度Geek说

swift 移动端 开发语言 企业号 5 月 PK 榜

还在服务器上捞日志?试试这款可视化监控系统吧,真香!

Java你猿哥

Java 日志 ssm 监控系统 Frostmourne

不吹不黑!阿里新产微服务架构进阶笔记我粉了!理论实战齐飞

做梦都在改BUG

Java 架构 微服务 Spring Cloud

Flutter三棵树系列之详解各种Key | 京东云技术团队

京东科技开发者

flutter key 企业号 5 月 PK 榜 localkey

Scrum的三个工件(产品Backlog、Sprint Backlog、产品增量 )

顿顿顿

Scrum 敏捷 敏捷开发管理 敏捷开发管理工具

红旗软件正式发布龙蜥社区版国产高可靠操作系统

OpenAnolis小助手

Linux 开源 龙蜥社区 红旗软件 社区版操作系统

为什么老有人想让我们“程序员”失业?征文获奖作品合集

InfoQ写作社区官方

技术专题合集 热门活动 三周年征文

现代应用开发模式:PWA vs 小程序

Onegun

小程序 PWA

重构复杂的React组件:编写高效且可读组件的5个最佳实践_大前端_Marco Antonio Ghiani_InfoQ精选文章