谈一谈建立React Component的几种方式

当咱们谈起React的时候,多半会将注意力集中在组件之上,思考如何将页面划分红一个个组件,以及如何编写可复用的组件。但对于接触React不久,尚未真正用它作一个完整项目的人来讲,理解如何建立一个组件也并不那么简单。在最开始的时候我觉得建立组件只须要调用createClass这个api就能够了;但学习了ES6的语法后,又知道了能够利用继承,经过extends React.component来建立组件;后来在阅读别人代码的时候又发现了PureComponent以及彻底没有继承,仅仅经过返回JSX语句的方式建立组件的方式。下面这篇文章,就将逐一介绍这几种建立组件的方法,分析其特色,以及如何选择使用哪种方式建立组件。javascript

几种方法

1.createClass

若是你尚未使用ES6语法,那么定义组件,只能使用React.createClass这个helper来建立组件,下面是一段示例:html

var React = require("react");
var Greeting = React.createClass({
  
  propTypes: {
    name: React.PropTypes.string //属性校验
  },

  getDefaultProps: function() {
    return {
      name: 'Mary' //默认属性值
    };
  },
  
  getInitialState: function() {
    return {count: this.props.initialCount}; //初始化state
  },
  
  handleClick: function() {
    //用户点击事件的处理函数
  },

  render: function() {
    return <h1>Hello, {this.props.name}</h1>;
  }
});
module.exports = Greeting;

这段代码,包含了组件的几个关键组成部分,这种方式下,组件的props、state等都是以对象属性的方式组合在一块儿,其中默认属props和初始state都是返回对象的函数,propTypes则是个对象。这里还有一个值得注意的事情是,在createClass中,React对属性中的全部函数都进行了this绑定,也就是如上面的hanleClick其实至关于handleClick.bind(this)java

2.component

由于ES6对类和继承有语法级别的支持,因此用ES6建立组件的方式更加优雅,下面是示例:react

import React from 'react';
class Greeting extends React.Component {

  constructor(props) {
    super(props);
    this.state = {count: props.initialCount};
    this.handleClick = this.handleClick.bind(this);
  }
  
  //static defaultProps = {
  //  name: 'Mary'  //定义defaultprops的另外一种方式
  //}
  
  //static propTypes = {
    //name: React.PropTypes.string
  //}
  
  handleClick() {
    //点击事件的处理函数
  }
  
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

Greeting.propTypes = {
  name: React.PropTypes.string
};

Greeting.defaultProps = {
  name: 'Mary'
};
export default Greating;

能够看到Greeting继承自React.component,在构造函数中,经过super()来调用父类的构造函数,同时咱们看到组件的state是经过在构造函数中对this.state进行赋值实现,而组件的props是在类Greeting上建立的属性,若是你对类的属性对象的属性的区别有所了解的话,大概能理解为何会这么作。对于组件来讲,组件的props是父组件经过调用子组件向子组件传递的,子组件内部不该该对props进行修改,它更像是全部子组件实例共享的状态,不会由于子组件内部操做而改变,所以将props定义为类Greeting的属性更为合理,而在面向对象的语法中类的属性一般被称做静态(static)属性,这也是为何props还能够像上面注释掉的方式来定义。对于Greeting类的一个实例对象的state,它是组件对象内部维持的状态,经过用户操做会修改这些状态,每一个实例的state也可能不一样,彼此间不互相影响,所以经过this.state来设置。git

用这种方式建立组件时,React并无对内部的函数,进行this绑定,因此若是你想让函数在回调中保持正确的this,就要手动对须要的函数进行this绑定,如上面的handleClick,在构造函数中对this 进行了绑定。es6

3.PureComponet

咱们知道,当组件的props或者state发生变化的时候:React会对组件当前的Props和State分别与nextProps和nextState进行比较,当发现变化时,就会对当前组件以及子组件进行从新渲染,不然就不渲染。有时候为了不组件进行没必要要的从新渲染,咱们经过定义shouldComponentUpdate来优化性能。例如以下代码:github

class CounterButton extends React.Component {
  constructor(props) {
    super(props);
    this.state = {count: 1};
  }

  shouldComponentUpdate(nextProps, nextState) {
    if (this.props.color !== nextProps.color) {
      return true;
    }
    if (this.state.count !== nextState.count) {
      return true;
    }
    return false;
  }

  render() {
    return (
      <button
        color={this.props.color}
        onClick={() => this.setState(state => ({count: state.count + 1}))}>
        Count: {this.state.count}
      </button>
    );
  }
}

shouldComponentUpdate经过判断props.colorstate.count是否发生变化来决定需不须要从新渲染组件,固然有时候这种简单的判断,显得有些多余和样板化,因而React就提供了PureComponent来自动帮咱们作这件事,这样就不须要手动来写shouldComponentUpdate了:编程

class CounterButton extends React.PureComponent {
  constructor(props) {
    super(props);
    this.state = {count: 1};
  }

  render() {
    return (
      <button
        color={this.props.color}
        onClick={() => this.setState(state => ({count: state.count + 1}))}>
        Count: {this.state.count}
      </button>
    );
  }
}

大多数状况下, 咱们使用PureComponent可以简化咱们的代码,而且提升性能,可是PureComponent的自动为咱们添加的shouldComponentUpate函数,只是对props和state进行浅比较(shadow comparison),当props或者state自己是嵌套对象或数组等时,浅比较并不能获得预期的结果,这会致使实际的props和state发生了变化,但组件却没有更新的问题,例以下面代码有一个ListOfWords组件来将单词数组拼接成逗号分隔的句子,它有一个父组件WordAdder让你点击按钮为单词数组添加单词,但他并不能正常工做:segmentfault

class ListOfWords extends React.PureComponent {
  render() {
    return <div>{this.props.words.join(',')}</div>;
  }
 }
 
class WordAdder extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      words: ['marklar']
    };
    this.handleClick = this.handleClick.bind(this);
  }
  
  handleClick() {
    // 这个地方致使了bug
    const words = this.state.words;
    words.push('marklar');
    this.setState({words: words});
  }

  render() {
    return (
      <div>
        <button onClick={this.handleClick} />
        <ListOfWords words={this.state.words} />
      </div>
    );
  }
}

这种状况下,PureComponent只会对this.props.words进行一次浅比较,虽然数组里面新增了元素,可是this.props.words与nextProps.words指向的还是同一个数组,所以this.props.words !== nextProps.words 返回的即是flase,从而致使ListOfWords组件没有从新渲染,笔者以前就由于对此不太了解,而随意使用PureComponent,致使state发生变化,而视图就是不更新,调了很久找不到缘由~。api

最简单避免上述状况的方式,就是避免使用可变对象做为props和state,取而代之的是每次返回一个全新的对象,以下经过concat来返回新的数组:

handleClick() {
  this.setState(prevState => ({
    words: prevState.words.concat(['marklar'])
  }));
}

你能够考虑使用Immutable.js来建立不可变对象,经过它来简化对象比较,提升性能。
这里还要提到的一点是虽然这里虽然使用了Pure这个词,可是PureComponent并非纯的,由于对于纯的函数或组件应该是没有内部状态,对于stateless component更符合纯的定义,不了解纯函数的同窗,能够参见这篇文章

4.Stateless Functional Component

上面咱们提到的建立组件的方式,都是用来建立包含状态和用户交互的复杂组件,当组件自己只是用来展现,全部数据都是经过props传入的时候,咱们即可以使用Stateless Functional Component来快速建立组件。例以下面代码所示:

import React from 'react';
const Button = ({
  day,
  increment
}) => {
  return (
    <div>
      <button onClick={increment}>Today is {day}</button>
    </div>
  )
}

Button.propTypes = {
  day: PropTypes.string.isRequired,
  increment: PropTypes.func.isRequired,
}

这种组件,没有自身的状态,相同的props输入,必然会得到彻底相同的组件展现。由于不须要关心组件的一些生命周期函数和渲染的钩子,因此不用继承自Component显得更简洁。

对比

createClass vs Component

对于React.createClassextends React.Component本质上都是用来建立组件,他们之间并无绝对的好坏之分,只不过一个是ES5的语法,一个是ES6的语法支持,只不过createClass支持定义PureRenderMixin,这种写法官方已经再也不推荐,而是建议使用PureComponent。

pureComponent vs Component

经过上面对PureComponent和Component的介绍,你应该已经了解了两者的区别:PureComponent已经定义好了shouldUpdateComponentComponent须要显示定义。

Component vs Stateless Functional component

  1. Component包含内部state,而Stateless Functional Component全部数据都来自props,没有内部state;

  2. Component 包含的一些生命周期函数,Stateless Functional Component都没有,由于Stateless Functional component没有shouldComponentUpdate,因此也没法控制组件的渲染,也便是说只要是收到新的props,Stateless Functional Component就会从新渲染。

  3. Stateless Functional Component 不支持Refs

选哪一个?

这里仅列出一些参考:

  1. createClass, 除非你确实对ES6的语法一窍不通,否则的话就不要再使用这种方式定义组件。

  2. Stateless Functional Component, 对于不须要内部状态,且用不到生命周期函数的组件,咱们可使用这种方式定义组件,好比展现性的列表组件,能够将列表项定义为Stateless Functional Component。

  3. PureComponent/Component,对于拥有内部state,使用生命周期的函数的组件,咱们可使用两者之一,可是大部分状况下,我更推荐使用PureComponent,由于它提供了更好的性能,同时强制你使用不可变的对象,保持良好的编程习惯。

参考文章

optimizing-performance.html#shouldcomponentupdate-in-action
pureComponent介绍
react-functional-stateless-component-purecomponent-component-what-are-the-dif
4 different kinds of React component styles
react-without-es6
react-create-class-versus-component

相关文章
相关标签/搜索