如何写出漂亮的 React 组件

更新日期: 2019-11-09阅读量: 1011标签: react

在Walmart Labs的产品开发中,我们进行了大量的Code Review工作,这也保证了我有机会从很多优秀的工程师的代码中学习他们的代码风格与样式。在这篇博文里我会分享出我最欣赏的五种组件模式与代码片。不过我首先还是要谈谈为什么我们需要执着于提高代码的阅读体验。就好像你有很多种方式去装扮一只猫,如果你把你的爱猫装扮成了如下这样子:

你或许可以认为萝卜青菜各有所爱,但是代码本身是应当保证其可读性,特别是在一个团队中,你的代码是注定要被其他人阅读的。电脑是不会在意这些的,不管你朝它们扔过去什么,它们都会老老实实的解释,但是你的队友们可不会这样,他们会把丑陋的代码扔回到你的脸上。而所谓的Pretty Components,应该包含如下的特性:

  • 即使没有任何注释的情况下也易于理解
  • 比乱麻般的代码有更好的性能表现
  • 更易于进行Bug追溯
  • 简洁明了,一句顶一万句


SFC:Stateless Functional Component

我觉得我们在开发中经常忽略掉的一个模式就是所谓的Stateless Functional Component,不过这是我个人最爱的react组件优化模式,没有之一。我喜爱这种模式不仅仅因为它们能够减少大量的模板代码,而且因为它们能够有效地提高组件的性能表现。总而言之,SFC能够让你的应用跑的更快,长的更帅。

直观来看,SFC就是指那些仅有一个渲染函数的组件,不过这简单的改变就可以避免很多的无意义的检测与内存分配。下面我们来看一个实践的例子来看下SFC的具体作用,譬如:


如果我们用正统的react组件的写法,可以得出如下代码:

export default class RelatedSearch extends React.Component {
  constructor(props) {
    super(props);
    this._handleClick = this._handleClick.bind(this);
  }
  _handleClick(suggestedUrl, event) {
    event.preventDefault();
    this.props.onClick(suggestedUrl);
  }
  render() {
    return (
      <section className="related-search-container">
        <h1 className="related-search-title">Related Searches:</h1>
        <Layout x-small={2} small={3} medium={4} padded={true}>
          {this.props.relatedQueries.map((query, index) =>
            <Link
              className="related-search-link"
              onClick={(event) =>
                this._handleClick(query.searchQuery, event)}
              key={index}>
              {query.searchText}
            </Link>
          )}
        </Layout>
      </section>
    );
  }
}

而使用SFC模式的话,大概可以省下29%的代码:

const _handleClick(suggestedUrl, onClick, event) => {
  event.preventDefault();
  onClick(suggestedUrl);
};
const RelatedSearch = ({ relatedQueries, onClick }) =>
  <section className="related-search-container">
    <h1 className="related-search-title">Related Searches:</h1>
    <Layout x-small={2} small={3} medium={4} padded={true}>
      {relatedQueries.map((query, index) =>
        <Link
          className="related-search-link"
          onClick={(event) =>
            _handleClick(query.searchQuery, onClick, event)}
          key={index}>
          {query.searchText}
        </Link>
      )}
    </Layout>
  </section>
export default RelatedSearch;

代码量的减少主要来源两个方面:

  • 没有构造函数(5行)
  • 以Arrow Function的方式替代Render语句(4行)

实际上,SFC最迷人的地方不仅仅是其代码量的减少,还有就是对于可读性的提高。SFC模式本身就是所谓纯组件的一种最佳实践范式,而移除了构造函数并且将_handleClick()这个点击事件回调函数提取出组件外,可以使jsX代码变得更加纯粹。另一个不错的地方就是SFC以Arrow Function的方式来定义了输入的Props变量,即以Object Destructring语法来声明组件所依赖的Props:

const RelatedSearch = ({ relatedQueries, onClick }) =>

这样不仅能够使组件的Props更加清晰明确,还能够避免冗余的this.props表达式,从而使代码的可读性更好。

最后,我还想要强调下虽然我很推崇SFC,不过也不能滥用它。最合适使用SFC的地方就是之前你用纯组件的地方。在Walmart Labs中,我们使用Redux来管理应用的状态,也就意味着我们绝大部分的组件都是纯组件,也就给了SFC广阔的应用空间。一般来说,有以下特征的组件式绝对不适合使用SFC的:

  • 需要自定义整个组件的生命周期管理
  • 需要使用到refs

Conditional Components

jsX本身不支持if表达式,不过我们可以使用逻辑表达式的方式来避免将代码切分到不同的子模块中,大概是如下样子:

render() {
  <div class="search-results-container">
    {this.props.isGrid
      ? <SearchResultsGrid />
      : <SearchResultsList />}
  </div>
}

这种表达式在二选一渲染的时候很有效果,不过对于选择性渲染一个的情况很不友好,譬如如下的情况:

render() {
  <div class="search-results-list">
    {this.props.isSoftSort
      ? <SoftSortBanner />
      : null
    }
  </div>
}

这样子确实能起作用,不过看上去感觉怪怪的。我们可以选用另一种更加语义化与友好的方式来实现这个功能,即使用逻辑与表达式然后返回组件:

render() {
  <div class="search-results-list">
    {!!this.props.isSoftSort && <SoftSortBanner />}
  </div>
}

不过这一点也是见仁见智,每个人按照自己的喜好来就行了。


Arrow Syntax In React And Redux

ES2015里包含了不少可口的语法糖,我最爱的就是那个Arrow Notation。这个特性在编写组件时很有作用:

const SoftSort = ({ hardSortUrl, sortByName, onClick }) => {
  return (
    <div className="SearchInfoMessage">
      Showing results sorted by both Relevance and {sortByName}.
      <Link
        href={`?${hardSortUrl}`}
        onClick={(ev) => onClick(ev, hardSortUrl)}>
        Sort results by {sortByName} only
      </Link>
    </div>
  );
};

该函数的功能就是返回JSX对象,我们也可以忽略return语句:

const SoftSort = ({ hardSortUrl, sortByName, onClick }) =>
  <div className="SearchInfoMessage">
    Showing results sorted by both Relevance and {sortByName}.
    <Link
      href={`?${hardSortUrl}`}
      onClick={(ev) => onClick(ev, hardSortUrl)}>
      Sort results by {sortByName} only
    </Link>
  </div>

代码行数又少了不少咯!

另一块我觉得非常适用Arrow Function的地方就是Redux的mapStateToProps函数:

const mapStateToProps = ({isLoading}) => {
  return ({
    loading: isLoading,
  });
};

需要注意的是,如果你返回的是Object,你需要包裹在大括号内:

const mapStateToProps = ({isLoading}) => ({
  loading: isLoading
});

使用Arrow Function优化的核心点在于其能够通过专注于函数的重要部分而提升代码的整体可读性,并且避免过多的模板代码带来的噪音。


合理使用Object Destructing与Spread Attributes

大的组件往往受困于this.props过长的窘境,典型的如下所示:

render() {
  return (
    <ProductPrice
      hidePriceFulfillmentDisplay=
       {this.props.hidePriceFulfillmentDisplay}
      primaryOffer={this.props.primaryOffer}
      productType={this.props.productType}
      productPageUrl={this.props.productPageUrl}
      inventory={this.props.inventory}
      submapType={this.props.submapType}
      ppu={this.props.ppu}
      isLoggedIn={this.props.isLoggedIn}
      gridView={this.props.isGridView}
    />
  );
}

这么多的Props估计看着都头疼,如果我们要将这些Props继续传入下一层,大概就要变成下面这个样子了:

render() {
  const {
    hidePriceFulfillmentDisplay,
    primaryOffer,
    productType,
    productPageUrl,
    inventory,
    submapType,
    ppu,
    isLoggedIn,
    gridView
  } = this.props;
  return (
    <ProductPrice
      hidePriceFulfillmentDisplay={hidePriceFulfillmentDisplay}
      primaryOffer={primaryOffer}
      productType={productType}
      productPageUrl={productPageUrl}
      inventory={inventory}
      submapType={submapType}
      ppu={ppu}
      isLoggedIn={isLoggedIn}
      gridView={isGridView}
    />
  );
}

暂时不考虑unKnown Props,我们可以使用解构赋值来实现这个功能:

render() {
  const props = this.props;
  return <ProductPrice {...props} />
}


Method Definition Shorthand

最后这个方法不一定多有用,不过还是能让你的代码变得更加漂亮。如果你希望在Object中添加函数,你可以使用ES2015 Method Definition Shorthand来代替传统的ES5的表达式,譬如:

Link.defaultProps = {
  onClick(event) {
    event.preventDefault();
    Logger.log(event);
  }
};

如果你想设置一个默认的空方法,也可以利用这种方式:

ProductRating.defaultProps = {
  onStarsClick() {}
};
站长推荐

1.云服务推荐: 国内主流云服务商,各类云产品的最新活动,优惠券领取。地址:阿里云腾讯云华为云

链接: https://www.fly63.com/article/detial/8910

React 项目结构和组件命名之道

React 作为一个库,不会决定你如何组织项目的结构。这是件好事,因为这样我们有了充分的自由去尝试不同的组织方式并且选取最适合我们的方式。但是从另一个角度讲,这可能会让刚刚上手 React 的开发者产生些许困惑。

选择 Reac​​tJS 的五大理由

ReactJS是一个开源的JavaScript库,并且由Facebook和Instagram这样的顶尖IT企业以及开发者社区所维护。该框架广泛使用于为web应用程序开发用户界面的时候。这个特殊的框架被发明时带有这样一个目的:

React组件的state和props

React 的数据是自顶向下单向流动的,即从父组件到子组件中,组件的数据存储在 props 和 state 中。实际上在任何应用中,数据都是必不可少的,我们需要直接的改变页面上一块的区域来使得视图的刷新

React常用hook的优化useEffect浅比较

先说说react原版的useEffect使用起来不便的地方,这里的effect每次更新都会执行,因为第三个参数一直是不等的,第二是在deps依赖很多的时候是真的麻烦

新手学习 react 迷惑的点

网上各种言论说 React 上手比 Vue 难,可能难就难不能深刻理解 JSX,或者对 ES6 的一些特性理解得不够深刻,导致觉得有些点难以理解,然后说 React 比较难上手,还反人类啥的

react中PureComponent浅对比策略

PureComponent实现了Component中没有实现的shouComponentUpdata()方法,会对state和props进行一次浅对比,本文介绍一下浅对比策略,源码中,实现浅对比的函数是:shallowEqual()

不要过度使用React.useCallback()

我博客的一位读者在Facebook上联系到我,提出了一个有趣的问题。他说,他的队友不管在什么情况下,都会把每一个回调函数封装在 useCallback() 里面。

21 项优化 React App 性能的技术

在 React 内部,React 会使用几项巧妙的小技术,来优化计算更新 UI 时,所需要的最少的更新 DOM 的操作。在大多数情况下,即使你没有针对性能进行专项优化,React 依然很快,但是仍有一些方法可以加速 React 应用程序

React 服务端渲染完美的解决方案

使用 React 构建客户端应用程序,默认情况下,可以在浏览器中输出 React 组件,进行生成 DOM 和操作 DOM。React 也可以在服务端通过 Node.js 转换成 HTML,直接在浏览器端“呈现”处理好的 HTML 字符串,这个过程可以被认为 “同构”

React大型项目状态管理库如何选型?

由于要做一个使用起来比较舒服的轮子,最近研究了下React的状态管理库,当然,仅限在使用层面,也就是用着舒服的角度来选择到底使用哪个状态管理库。本着在Github上面看看React社区内状态管理库的流行程度和使用程度的层面

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!