25

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

 4 years ago
source link: https://www.tuicool.com/articles/77NNFbV
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

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

问题

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

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

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

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

脏组件长什么样

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

复制代码

importReactfrom'react';
import'./Listcomponent.css';

classListcomponentextendsReact.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 允许你复用状态逻辑,而无需更改组件层次结构。

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

复制代码

importReact, { useState }from'react';
import'./ListComponent.css';
constListComponent =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>
<ListItemtitle="Create"action={create}setClicked={setClicked}/>
<ListItemtitle="Read"action={read}setClicked={setClicked}/>
<ListItemtitle="Update"action={update}setClicked={setClicked}/>
<ListItemtitle="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. 切成小块

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

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

复制代码

importReact, { useState }from'react';
importPropTypesfrom'prop-types';
importListItemfrom'./ListItem.js'

const List = ({create,read,update, destroy }) => {
const [clicked, setClicked] = useState('');

return(
<div>
<hl>The last clicked buttonis{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,
};

exportdefaultList;
List.js

复制代码

importReact, { useState }from'react';
importPropTypesfrom'prop-types';

constListItem =({ 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
};
exportdefaultListItem;
ListItem.js

小结

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

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

作者介绍:

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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK