速来报名!AICon北京站鸿蒙专场~ 了解详情
写点什么

重构复杂的 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:323439
用户头像
王文婧 InfoQ编辑

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

关注

评论 1 条评论

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

最右×微帧,高质量的HEIF图片编码压缩技术

微帧Visionular

计算机视觉 HEIF 视频编解码 图片压缩 WebP

【LeetCode】数位和相等数对的最大和Java题解

Albert

LeetCode 7月月更

KusionStack 开源|Kusion 模型库和工具链的探索实践

SOFAStack

编程语言 开源项目 运维技术 自主研发 项目共建

iOS中内存管理(ARC)

NewBoy

ios 前端 移动端 iOS 知识体系 7月月更

精品方案|海泰云密码应用服务解决方案 打造安全合规的云上应用

电子信息发烧客

什么?多商户系统不适配 APP?这不就来了么!

CRMEB

CentOS 8里的这个功能,天翼云SFS弹性文件校准了

天翼云开发者社区

Centos 7 CentOS 8 弹性文件

阿里云发布《升舱-数据仓库升级交付标准化》白皮书

Lily

亮点抢先看!2022 开放原子全球开源峰会定于 7 月 25-29 日在北京举办

kk-OSC

开源 开发原子全球开源峰会

在 Excel 内使用 ODBC 消费 SAP ABAP CDS view

汪子熙

JDBC SAP abap ODBC 7月月更

视频分析StreamEye Studio

贾献华

7月月更

Web3 基础设施 NFTScan 浏览器对区块链行业的价值与意义

NFT Research

区块链 Web3.0

后深度学习时代,推荐系统向何处去?

博文视点Broadview

让你事半功倍的JS utils工具函数

南城FE

JavaScript 前端 工具库 7月月更

数仓之数据质量建设

五分钟学大数据

数据仓库 数据治理 数据质量 7月月更

云原生(八) | Devops篇之深入Devops

Lansonli

云原生 7月月更

算法题每日一练---第3天:一步之遥

知心宝贝

算法 前端 后端 云开发 7月月更

绿色低碳天翼云,数字经济新引擎!

天翼云开发者社区

云计算 大数据 AI 数字化转型

云生态大会,随“峰”而来!

天翼云开发者社区

被大厂强制毕业,两个月空窗期死背八股文,幸好上岸,不然房贷都还不上了

程序知音

Java 程序员 java面试 后端技术 八股文

出自阿里P8的Java面试神册,涵盖30个技术栈扛住面试官的狂轰乱炸

程序知音

Java 面试 程序员面试 后端技术 Java八股文

如何提高LED显示屏清晰度?

Dylan

LED显示屏 led显示屏厂家

C#/VB.NET在 Word 中插入水印

Geek_249eec

C# word 添加水印 VB.NET

C++课程设计:图书管理系统【附源码】

攻城狮杰森

c++ 7月月更 课程设计 图书管理系统

Wallys/3×3/2×2 MIMO/ 802.11ac/ Mini PCIe /2,4GHz / 5GHz QCA 9880

wallys-wifi6

Wallys/DR882/QCA9882/ AC/AN MiniPCIE/2×2.4GHz 2x5GHz MT7915 MT7975

wallys-wifi6

QCA9880 QCA9882 MT7915 MT7975 /

用 emoji 学安全上网小常识?看 Google 新玩法

Geek_2d6073

解密方舟的高性能内存回收技术——HPP GC

HarmonyOS开发者

HarmonyOS

图的基本定义和相关概念(一)

乔乔

7月月更

RKE vs. RKE2:对比两种 Kubernetes 发行版

Rancher

Kubernetes k8s rancher

TiFlash 源码阅读(五) DeltaTree 存储引擎设计及实现分析 - Part 2

PingCAP

TiDB TiDB 源码解读

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