如何写出漂亮的React组件

本文翻译自Make-Your-React-Components-Pretty。欢迎转载,注明出处。
本文从属于笔者的Web前端入门与最佳实践 中的React入门与最佳实践系列,同类型文章还包括React 代码风格约定前端

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

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

  • 即便没有任何注释的状况下也易于理解github

  • 比乱麻般的代码有更好的性能表现less

  • 更易于进行Bug追溯ide

  • 简洁明了,一句顶一万句函数

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() {}
};
相关文章
相关标签/搜索