ES6+開發React元件

幕三少發表於2016-05-09

在這裡簡要的說一下這些語言新特性對 React 應用的開發有什麼影響,這些 ES6+ 特性使得 React 開發更簡單更有趣。

迄今為止,最能體現我們使用 ES6+ 來編寫 React 元件的就是我們選擇使用類定義語法。替代了使用 React.createClass 方法來定義一個元件,我們可以定義一個 bonafide ES6 類來擴充套件 React.Component:

1
2
3
4
5
class Photo extends React.Component {
  render() {
    return <img alt={this.props.caption} src={this.props.src} />;
  }
}

現在,你就會發現一個微妙的差異 —— 當使用定義類的時候語法更簡潔:

1
2
3
4
5
// The ES5 way
var Photo = React.createClass({
  handleDoubleTap: function(e) { … },
  render: function() { … },
});
1
2
3
4
5
// The ES6+ way
class Photo extends React.Component {
  handleDoubleTap(e) { … }
  render() { … }
}

值得關注的是,我們去掉了兩個括號和一個分號,每個方法宣告我們省略了一個冒號,一個關鍵字和一個分號。

當使用新的類定義時,所有的生命週期方法至少有一個是符合你期望的。類的 constructor 現在假設 role 之前是通過 componentWillMount 填充的:

1
2
3
4
// The ES5 way
var EmbedModal = React.createClass({
  componentWillMount: function() { … },
});
1
2
3
4
5
6
7
// The ES6+ way
class EmbedModal extends React.Component {
  constructor(props) {
    super(props);
    // Operations usually carried out in componentWillMount go here
  }
}

屬性初始化程式

在 ES6+ 類的世界裡,prop types 和 defaults live 在類自身作為靜態屬性。這些,在元件的初始化狀態也是一樣的,可以使用 ES7 property initializers 定義:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// The ES5 way
var Video = React.createClass({
  getDefaultProps: function() {
    return {
      autoPlay: false,
      maxLoops: 10,
    };
  },
  getInitialState: function() {
    return {
      loopsRemaining: this.props.maxLoops,
    };
  },
  propTypes: {
    autoPlay: React.PropTypes.bool.isRequired,
    maxLoops: React.PropTypes.number.isRequired,
    posterFrameSrc: React.PropTypes.string.isRequired,
    videoSrc: React.PropTypes.string.isRequired,
  },
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// The ES6+ way
class Video extends React.Component {
  static defaultProps = {
    autoPlay: false,
    maxLoops: 10,
  }
  static propTypes = {
    autoPlay: React.PropTypes.bool.isRequired,
    maxLoops: React.PropTypes.number.isRequired,
    posterFrameSrc: React.PropTypes.string.isRequired,
    videoSrc: React.PropTypes.string.isRequired,
  }
  state = {
    loopsRemaining: this.props.maxLoops,
  }
}

ES7 屬性初始化程式操作內部類的 constructor,this 指向 construction 的類例項,所以初始化狀態可以依賴於 this.props。值得關注的是,我們不再定義 prop 預設值和使用 getter 函式初始化狀態物件。

Arrow 函式

React.createClass 方法用來在你的元件例項方法中執行一些額外的繫結工作,為了確保 this 關鍵字會指向元件例項: 

1
2
3
4
5
6
7
// Autobinding, brought to you by React.createClass
var PostInfo = React.createClass({
  handleOptionsButtonClick: function(e) {
    // Here, `this` refers to the component instance.
    this.setState({showOptionsModal: true});
  },
});

自從我們不參與 React.createClass 方法,而是使用 ES6+ 類語法定義元件,看似需要手動繫結例項方法:

1
2
3
4
5
6
7
8
9
10
11
12
// Manually bind, wherever you need to
class PostInfo extends React.Component {
  constructor(props) {
    super(props);
    // Manually bind this method to the component instance...
    this.handleOptionsButtonClick = this.handleOptionsButtonClick.bind(this);
  }
  handleOptionsButtonClick(e) {
    // ...to ensure that `this` refers to the component instance here.
    this.setState({showOptionsModal: true});
  }
}

幸運的是,通過繫結兩個 ES6+ 特性 – arrow functions 和屬性初始化程式  – 可以選擇繫結元件例項:

1
2
3
4
5
class PostInfo extends React.Component {
  handleOptionsButtonClick = (e) => {
    this.setState({showOptionsModal: true});
  }
}

ES6 的 arrow 函式體分享相同的詞  this,用這來圍繞他們的程式碼,這些可以達到我們預期的結果,也是 ES7 屬性初始化程式在域內的方式。 Peek under the hood 來看看為什麼能實現。

動態屬性名稱 & 模板字串

其中一個物件常量增強是可以分配到一個派生屬性名稱。我們最初可能會像下面這樣設定一些狀態:

1
2
3
4
5
6
7
var Form = React.createClass({
  onChange: function(inputName, e) {
    var stateToSet = {};
    stateToSet[inputName + `Value`] = e.target.value;
    this.setState(stateToSet);
  },
});

現在,我們有能力構造通過一個執行時 JavaScript 表示式確定屬性名稱的物件。這裡,我們使用了一個模板字串來確定哪個屬性設定狀態:

1
2
3
4
5
6
7
class Form extends React.Component {
  onChange(inputName, e) {
    this.setState({
      [`${inputName}Value`]: e.target.value,
    });
  }
}

解構 & 傳播屬性

通常在編寫元件的時候,我們可能想把大部分父元件的 props 傳遞給子元件,但不是所有。結合 ES6+ 解構和 JSX 傳播屬性,這個不需要多餘的部分就能實現:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class AutoloadingPostsGrid extends React.Component {
  render() {
    var {
      className,
      ...others,  // contains all properties of this.props except for className
    } = this.props;
    return (
      <div className={className}>
        <PostsGrid {...others} />
        <button onClick={this.handleLoadMoreClick}>Load more</button>
      </div>
    );
  }
}

我們可以結合 JSX 傳播屬性和常規屬性,利用一個簡單的優先原則實現 overrides 和 defaults。這個元素會要求 className “override” 甚至是在 this.props 存在 className 屬性: 

1
2
3
<div {...this.props} className="override">
   … 
</div>

這個元素常規來說需要 className “base” ,除非 this.props 有 className 屬性覆蓋: 

1
2
3
<div className="base" {...this.props}>
   … 
</div>

希望大家能享受 ES6+ 語言特性給 React 開發帶來的一些便利。


相關文章