將 React 組件 由 createClass 重構為 ES6 寫法(譯)


原文地址: http://www.newmediacampaigns.com/blog/refactoring-react-components-to-es6-classes

正文

我們團隊是 React框架 的忠實粉絲,並且已經嘗試使用 ES6 來進行 React 開發。很高興看到從 React 0.13 開始,官方推薦使用 ES6 類語法來進行組件的定義。

將 React 組件從 React 0.12 通過 createClass 定義的組件重構為基於 0.13 版本使用 ES6 類定義的組件只需要以下簡單的幾步。在后文中將一步一步的給予說明。

第一步 - 從組件構造函數中取出 propTypesgetDefaultProps 放到組件屬性上

createClass 這個 API 期望的參數為一個字面量對象,同時可以定義方法和屬性。而 ES6 中的類定義只允許定義方法,而不允許定義屬性(The committee's rationale for this was primarily to have a minimal starting point for classes which could be easily agreed upon and expanded in ES7)。因為這個原因對於 propTypes 這樣的屬性,我們必須將其定義在 類定義 之外(we must define them outside of the class definition)。

另外的一個變化之處是在 React 0.13 版本中, props 屬性被定義為 immutable data(不可變數據),所以 getDefaultProps 作為一個函數將不能在繼續使用,因此也需要將其重構到構造函數之外。

Before:

var ExampleComponent = React.createClass({
    propTypes: {
      aStringProps: React.PropTypes.string
    },
    getDefaultProps: function() {
      return { aStringProps: '' }
    }
})

After:

var ExampleComponent = React.createClass({...});
ExampleComponent.propTypes = {
  aStringProps: React.PropTypes.string
};
ExampleComponent.defaultProps = {
  aStringProps: ''
}

第二步 - 從 createClass 到使用 ES6 Class 語法定義組件

ES6 Class 語法比起傳統的對象字面量更為簡潔。對於方法的定義不需要再使用 function 關鍵字,也不需要 , 來分開多個方法。

Before:

var ExampleComponent = React.createClass({
  render: function() {
    return <div onClick={ this._handleClick }>Hello World!</div>
  },
  _handleClick: function() {
    console.log(this);
  }
})

After:

class ExampleComponent extends React.component {
  render() {
    return <div onClick={ this._handleClick }>Hello World!</div>
  }
  _handleClick() {
    console.log(this)
  }
}

第三步 - 綁定實例方法和回調到實例上

使用 createClass 時有一個便捷之處是默認的就將提供的方法綁定到了組件實例上。比如前面例子中的 _handleClick 方法里的 this 將會指向當前這個組件實例。當使用 ES6 Class 語法的時候,我們必須要手動為這些方法綁定執行上下文。React 團隊推薦將綁定工作放在構造函數內完成(This is a stopgap until ES7 allows property initializers)

Before:

class ExampleComponent extends React.Component {
  render() {
    return <div onClick={ this._handleClick }> Hello World! </div>
  }
  _handleClick() {
    console.log(this);    // this is undefined
  }
}

After:

class ExampleComponent extends React.Component {
  constructor() {
    super();
    this._handleClick = this._handleClick.bind(this);
  }
  render() {
    return <div onClick={ this._handleClick }> Hello World! </div>
  }
  _handleClick() {
    console.log(this);  // this is an ExampleComponent
  }
}

在文章結尾將會介紹我們對 Component 類的擴展,該類將會更好的實現自動綁定的過程。

第四步 - 將初始 state 定義移動到構造函數中

React 團隊推薦使用一種更符合語義的方法來定義初始 state(在構造函數中將初始值存放在對應屬性上)。也就是可以在構造函數中將 getInitialState 方法中的返回值賦值到 該實例對象的 this.state 屬性上。

Before:

class ExampleComponent extends React.Component {
  getInitialState() {
    return Store.getState();
  }
  constructor() {
    super();
    this._handleClick = this._handleClick.bind(this);
  }
  // ...
}

After:

class ExampleComponent extends React.Component {
  constructor() {
    super();
    this._handleClick = this._handleClick.bind(this);
    this.state = Store.getState();
  }
  // ...
}

Conclusion

使用上面提到的少許的幾步將一個存在的 React 組件轉化為 ES6 Class 語法定義的組件是很簡單的。同時直到 JavaScript 語法上有 mixins 特性之前使用 React.createClass 也是不被反對的。

Bonus Step - 更簡潔的 this 綁定方法:

Before:

class ExampleComponent extends React.Component {
  constructor() {
    super();
    this._handleClick = this._handleClick.bind(this);
    this._handleFoo = this._handleFoo.bind(this);
  }
  // ...
}

After:

class BaseComponent extends React.Component {
  _bind(...methods) {
    methods.forEach( (method) => this[method] = this[method].bind(this) );
  }
}

class ExampleComponent extends BaseComponent {
  constructor() {
    super();
    this._bind('_handleClick', '_handleFoo');
  }
  // ...
}

我們通過 _bind 方法將重復的綁定方法提取到 BaseComponent 類上使綁定的方法更為簡潔。_bind 方法使用了很多 ES6 的特性: methods 參數使用了 rest parameterforEach 里面使用了 箭頭函數(arrow function).


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM