首页
学习
活动
专区
工具
TVP
发布

重构复杂的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

  • 发表于:
  • 本文为 InfoQ 中文站特供稿件
  • 首发地址https://www.infoq.cn/article/Ry4IcKy5CRB1PoKVi0qL
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券