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的區別

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容