写点什么

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

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

关注

评论 1 条评论

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

面向对象基础

断墨寻径

面向对象 java;

追求极致性能!RocketMQ消息通信详解

Java全栈架构师

Java 程序员 面试 RocketMQ 消息中间件

从零到一构建完整知识体系!阿里巴巴Java并发编程技术内幕全网首次公开

Java全栈架构师

源码 程序员 程序人生 Java并发 java面试

4.0体验站|我对OceanBase 4.0社区版的体验与看法

OceanBase 数据库

让迁移不再开盲盒,让云也能省钱丨Hackathon 项目背后的故事第一期回顾

PingCAP

hackathon

数字先锋| 教育资源乘云而来!46万城乡学子共享名师课堂

天翼云开发者社区

【C语言】for 关键字

謓泽

11月月更

Kubectl 命令总结

蜗牛也是牛

TiKV 源码阅读三部曲(三)写流程

PingCAP

源码阅读

手慢无!清华大牛熬夜整理Spring微服务架构设计第2版文档,限时删

钟奕礼

Java 编程 架构 计算机 java程序员

三面阿里java后台开发岗总结:进阿里必看这份究极面试文档

钟奕礼

Java 编程 java程序员 java面试 java架构

共享开源技术,共建开放生态丨平凯星辰余梦杰出席 2022 世界互联网大会开源论坛圆桌对话

PingCAP

开源

Alibaba最新推出的Spring Cloud手册惨遭开源

小小怪下士

Java 程序员 阿里 SpringCloud

太强了!终于有人整理出了仿京东电商项目,再次开源了

钟奕礼

Java 编程 架构 项目 java程序员

欢迎来嫖!阿里P8高级技术专家携这份818页Java核心技术重磅来袭

钟奕礼

Java 编程 计算机 java程序员 java架构

视频服务HDR Vivid 还原色彩,让所见成“真”

HMS Core

视频 HMS Core

双11狂欢背后,火山引擎数智平台为品牌做了这件事

字节跳动数据平台

大数据 营销数字化 火山引擎

Jmix 1.4 功能概览

世开 Coding

企业级低代码 Jmix 企业级应用程序开发

高可用性集群软件就选Skybility HA!优势多多!

行云管家

高可用 双机热备

互联网公司网络堡垒机首选哪家品牌?有什么优势?

行云管家

互联网 网络安全 信息安全 堡垒机

旺链科技出席Hyperledger区块链技术峰会,分享数字乡村新业态

旺链科技

区块链 hyperledger 产业区块链 企业号十月PK榜

【#HDC2022】HarmonyOS体验官活动正式开启,赶快投稿赢限量奖品吧!

HarmonyOS开发者

HarmonyOS

java文件流

hello java

文件 程序 Java core 11月月更

OpenHarmony开发之MQTT讲解

OpenHarmony开发者

OpenHarmony

LED透明屏焊接和插接安装以及三招提升稳定性

Dylan

LED LED显示屏 led显示屏厂家

Linux内存泄露案例分析和内存管理分享

京东科技开发者

负载均衡 集群 内存泄漏 Linux Cron 运维、

天翼云实时云渲染,助力打造世界VR产业大会云上之城

天翼云开发者社区

如何杜绝 spark history server ui 的未授权访问?

明哥的IT随笔

hadoop spark

阿里云研发工程师刘睿:阿里云消息生态及最佳实践

云布道师

阿里云 云原生

想要设计一个良好的接口至少要考虑这14点!

程序员小毕

Java 编程 程序员 程序人生 java面试

这次,听人大教授讲讲分布式数据库的多级一致性|TDSQL关键技术突破

腾讯云数据库

腾讯云 tdsql 腾讯云数据库 多级一致性 中国人民大学

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