React:createClass 和 extends Component的区别

原文链接

写React的时候,你是应该用React.createClass语法还是ES6的class语法呢?或者都不?这篇文章将会解释一些二者之间的差异来帮助你做选择。

React可以用ES5或ES6完美书写。

使用JSX意味着你将通过Babel将JSX通过“build”转化为React.createElement调用。很多人利用这个特点在Babel的编译列表中添加一些es2015的语法以此来让ES6完全可用。

如果你正在使用Quik或是React Heatpack,这已经为你配置好了(如果你还没有安装环境,请阅读quick start React)。

比较 createClass 和 class

以下是使用React.createClass和ES6的class书写相同的组件:

var InputControlES5 = React.createClass({
  propTypes: {
    initialValue: React.PropTypes.string
  },
  defaultProps: {
    initialValue: ''
  }, // Set up initial state
  getInitialState: function() {
    return {
      text: this.props.initialValue || 'placeholder' 
    };
  },
  handleChange: function(event) {
    this.setState({
      text: event.target.value
    });
  },
  render: function() {
    return (
      <div>
        Type something:
        <input onChange={this.handleChange} 
          value={this.state.text} />
        </div> 
      );
    }
});

class InputControlES6 extends React.Component { 
  constructor(props) {
    super(props); 
    // Set up initial state
    this.state = {
      text: props.initialValue || 'placeholder' 
    };
    // Functions must be bound manually with ES6 classes
    this.handleChange = this.handleChange.bind(this); 
  } 
  handleChange(event) {
    this.setState({ 
      text: event.target.value
    });
  } 
  render() {
    return (
      <div>
        Type something:
        <input onChange={this.handleChange}
          value={this.state.text} />
      </div>
    );
  }
}
InputControlES6.propTypes = { 
  initialValue: React.PropTypes.string
};
InputControlES6.defaultProps = {
  initialValue: ''
};

以下是一些关键的不同点:

函数的绑定

这或许是最大的一个变化点

使用createClass, 每一个函数属性都会被React自动绑定。指的是this.whateverFn这样的函数在任何你需要调用的时候都会自动为你绑定正确的this

在ES6的class中,就不同了: 函数不再被自动绑定。你需要手动去绑定它们。最好的地方就是和以上例子一样,在构造函数里。

如果你不想非要手动打印所有的绑定操作,也可以通过react-autobind或者autobind-decorator来检查。

另一种方式就是在你使用的地方通过内联来绑定:

// Use `.bind`:
  render() { 
    return (
      <input onChange={this.handleChange.bind(this)}
        value={this.state.text} /> 
    );
}
// --- OR ---

// Use an arrow function:
render() {
  return (
    <input onChange={() => this.handleChange()} 
      value={this.state.text} />
  );
}

以上任意一种都可以,然而在效率上却不行了。每一次调用render(可以说是非常频繁!)一个新的函数都会被创建。与在构造函数里只绑定一次相比就慢一些。

最终的选择是使用箭头函数直接替换函数在类中的声明,像这样:

// the normal way
// requires binding elsewhere
handleChange(event) {
  this.setState({
    text: event.target.value
  });
}
// the ES7 way
// all done, no binding required
handleChange = (event) => { 
  this.setState({
    text: event.target.value
  });
}

通过这种方式,你不需要绑定任何东西。这都已经通过神奇的箭头函数被搞定了。像期望的那样,函数内部的this将会指向组件实例。

唯一的注意的地方就是这仅是一个"试验性" 的特性,意味着这不是官方的ES6规范。但是这依旧可以通过设置babel为"stage-0"来支持。如果你喜欢这种语法(被称作“把handleChange设置为带event的箭头函数”),试试吧。

构造函数应该调用super

ES6类的constructor需要接收props并且调用super(props)。这是createClass所没有的一点。

class和createClass的比较

这个很明显,一个调用React.createClass并传入一个对象,另一个则是使用class继承React.Component

小提示: 导入Component时如果在一个文件里包含多个组件,可以直接通过以下方式节省一些代码量:
import React, {Component} from 'react'

初始化State的设置

createClass 接受一个只会在组件被挂载时才会调用一次的initialState函数。

ES6 的class则使用构造函数。在调用super之后,可以直接设置state。

propTypes 和 defaultProps 的设置

使用createClass时,在传入的对象上定义一个propTypesdefaultProps作为属性。

使用ES6的类,这些将会成为类本身的属性,因此它们可以在类定义之后补充上。

如果你开启了ES7属性初始化,这里还有些捷径:

class Person extends React.Component { 
  static propTypes = { 
    name: React.PropTypes.string, 
    age: React.PropTypes.string 
  }; 
  static defaultProps = { 
    name: '', 
    age: -1 
  }; 
  ...
}

第三种选择

createClassclass以外,React也支持“无状态函数式组件”。基本上,这仅是一个不能有state的函数,并且它不能使用像componentWillMountshouldComponentUpdate这样的任何生命周期方法。无状态函数式组件对仅仅接受并依赖props渲染的简单组件来讲很棒,以下是几个例子:

function Person({firstName, lastName}) { 
  return ( 
    <span>{lastName}, {firstName}</span> 
  );
}

这利用ES6的解构将传入的props分解开来,但也可以这么写:

function Person(props) { 
  var firstName = props.firstName; 
  var lastName = props.lastName; 
  return ( 
    <span>{lastName}, {firstName}</span> 
  );
}

用哪个才对呢?

Facebook已经声明了React.createClass将最终会被ES6的classes替代,但是他们也说“在我们找到当前mixin所使用的例子的替代者以及在语言上支持类属性的初始化器前,我们不打算废弃React.createClass”。

在任何可以使用无状态函数式组件的地方使用它。简单而且会强制性地简化你的组件。

对于一些需要state,生命周期方法或是通过ref来操作潜在的DOM节点的复杂组件来讲,请使用class。

尽管知道这三种写法风格很棒,但在StackOverflow或者其他地方查找解决方案时可能仍会看到一些混杂着ES5和ES6写法的答案。ES6风格已经非常流行了但这不会是你看到的唯一一种写法。

From:React: ES5 (createClass) 还是 ES6 (class)? (译)
可参考:createClass 和 extends Component的区别

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,214评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,307评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,543评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,221评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,224评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,007评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,313评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,956评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,441评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,925评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,018评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,685评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,234评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,240评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,464评论 1 261
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,467评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,762评论 2 345

推荐阅读更多精彩内容