第六卷 - React.createClass 跟 extends Component 的差別


我買的 React 參考書,網路上的範例,真是各式各樣詭異,就以建立一個簡單的 Component 來說,就看了兩種表示方式:

這一種:

var MainScene = React.createClass({
  render() {
    // do something
    return;
  }
})

以及這一種:

class MainScene extends Component {
  render() {
    // do something
    return;
  }
}

簡單來說就是一個是 ES5 一個是 ES6,只是這樣看書跟看文件好不方便,都要自己腦中幫忙 compile 成 ES6…。

差別

1. 一個是使用 ES6 的 Class 來建立一個 React Component


假如你是單純使用 React 而非 React Native,若要使用 ES6 class 的方式去定義 component,可能你會需要類似 Babel 這個工具幫你把 ES6 轉成 ES5,因為有些 browser 不支援 ES6 哦!但是在寫 React Native 的話比較少會有這種需求吧?! (<-我猜的),畢竟 React Native 是 run 在 Mobile Device 上, browser 的問題應該不常見。

在寫 ES6 的 class,(說真的這個 class 的寫法真的有點像在寫其他物件導向的語言),可以定義 constructor:

import React, {
  AppRegistry,
  Component,
  StyleSheet,
  Text,
  View,
  NavigatorIOS,
  TouchableHighlight
} from 'react-native';

class NavIsProj extends Component {
  constructor(props) {
    super(props);
  }

  render() {
    return (
      <View>Hello</View>
    );
  }
}

AppRegistry.registerComponent('NavIsProj', () => NavIsProj);

constructor 會先 call 父類別的 constructor,super(props),之後再做你自己想做的事情 XD。

其實若沒有要覆寫 constructor 也不需要特別定義一次 constructor,我看很多 sample 也都沒有刻意去寫 constructor,不過如果要是初始化一些 state,有的會在 constructor 裡面做 initial。不過這個做法蠻有趣的就是,如果是用 ES6 定義 class 的方式,可以把 state 的初始化寫在 constructor:

class DemoComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = { /* initial state */ };
  }
}

如果是回到 ES5 用 React.classClass 的方式,則 state 可定義在 getInitialState():

var DemoComponent = React.createClass({
  getInitialState() {
    return { /* initial state */ };
  },
});

題外話了。


2. “this” 不一樣

  1. React.createClass 會自動把 this bind 進去,原理是 React.createClass 會把所有的方法都 bind 一遍。

  2. React Component 可就不一樣了… ,class 的 property 不會自動 bind 到 class 的 instance,所以你要自己 bind。

在一個 function 結尾的地方 .bind(this) 的意思是,讓 this 指向該 obj,如果是自己在 class 裡面新增的 function,你可能會發現在新增的 function 裡,呼叫 this.state 或是 this.什麼東西,會拿不到物件資料的情況(只是假設),因為在這個 function 裡面的 this 可能只是 undefined 還是什麼 null is not an object,所以透過 bind,把外面的 this (該 component 的 instance) 指向這個新的 function 的 this。


bind 的方式:

(1) 一個是呼叫的時候直接在方法的後面 .bind(this),這是一種 inline 的 bind 法。

You can use bind() to preserve this。- 官網

class NavPrj extends Component {
  render() {
    return (
      <Navigator
        initialRoute={id:'demo'}
        renderScene={this.renderScene.bind(this)} /> //<-  bind(this)
      );
  }

  renderScene(route, navigator) {
    return (
      <DetailPage navigator={navigator} />
    );
  }
}

(2) 另一個方式是,在 constructor 就處理完要被 bind 的 function,有的人喜歡這種方式,因為上一個 inline 的 bind 法,等於是寫在 jsx 上,而在 constructor 處理完這些 function 的 context,可以跟 jsx 做分離。

We recommend that you bind your event handlers in the constructor so they are only bound once for every instance。 官網

class DemoComponent extends Component {
  constructor(props) {
    super(props);
    this.state = {
      demo: 'somevalue',
    };
    // 在這裡 bind 好 this.renderRow !
    this.renderRow = this.renderRow.bind(this);
  }
    
  render() {
    var content = 
        <ListView
          demoVal={this.state.demo}
          renderRow={this.renderRow}/>;  // <- 這裡就不用 bind !
     
    return (<View> { content } </View>);
  }

  renderRow(repo: Object) {
     // do something
     return something;
  }

}

(3) 用 arror function

但 Arror Function 是把當下 scope 的 this 拋進去。

example:

return ( 
  <View style={styles.container}>
    <TextInput
      //onEndEditing={this.onSearchChange.bind(this)}
      onEndEditing={(e)=>this.onSearchChange(e)}
    />
    { content }
  </View>
);

onSearchChange(event: Object) {
  // 略
}

其實我後來覺得,與其 google 這兩個有什麼不同,一開就看如何從 React Component refactor 成 ES6 的 class,就一目了然了。

參考文章


創用 CC 授權條款
React Native 學習之旅winwu製作,以創用CC 姓名標示-非商業性-禁止改作 3.0 台灣 授權條款釋出。