React


Mixin


7.1 Mixin とは

var Timer = React.createClass({
  getInitialState: function(){
    return {secondsElapsed:0};
  },
  tick: function(){
    this.setState({secondsElapsed:this.state.secondsElapsed + 1});
  },
  componentDidMount: function(){
    this.interval = setInterval(this.tick, 1000);
  },
  componentWillUnmount: function(){
    clearInterval(this.interval);
  },
  render: function(){
    return (
      
Seconds Elapsed: {this.state.secondsElapsed}
); } });

このコンポーネントは単体では問題ないが タイマーを使うコンポーネントがほかにある場合、 それらすべてを実装すると結果的に同じコードが重複してしまいます。 そこでMixinの出番です

var Timer = React.createClass({
  // mixinの定義
  mixins: [IntervalMixin(1000)]
  getInitialState: function(){
    return {secondsElapsed:0};
  },
  onTick: function(){
    this.setState({secondsElapsed:this.state.secondsElapsed + 1});
  },
  render: function(){
    return (
      
Seconds Elapsed: {this.state.secondsElapsed}
); } });

mixins内の複数のオブジェクト間でメソッドが重複していた場合

React.createClass({
  mixins: [
    {
      getInisitalState: function(){ return {a:1}}
    },
    {
      getInisitalState: function(){ return {b:2}}
    }
  ]
});
戻り値は {a:1, b:2} となる。 

キーの名前が重複している場合はエラーとなる

先の例に戻り intervalMixin関数の実装を考えます

多くの場合、mixins の値として Mixinオブジェクトをその場で定義します。

複雑なオブジェクトの場合は、別途 Mixinオブジェクトを返す関数を定義します。

var intervalMixin = function(interval){
  return {
    componentDidMount: function(){
      this.__interval = setinterval(this.onTick, interval)
    },
    componentWillUnmount: function(){
      clearInterval(this.__intrval)
    }
  };
}

Mixin オブジェクトは分かりやすい反面、制約がある。

複数のタイマーを持つことができない

タイマーイベントのハンドラメソッド名は onTick に固定されている

タイマーを止めるには、内部のプロパティ __interval に直接アクセスする必要がある

var intervalMixin = {
  setInterval: function(callback, interval){
    var token = setInterval(callback, interval);
    this.__intervals.push(token);
  },
  componentDidMount: function(){
    this.__intervals = [];
  },
  componentWillUnmount: function(){
    this.__intervals.map(clearInterval);
  } 
};
// 2014/01/01からの経過秒数を表示するコンポーネント
var Since2014 = React.createClass({
  mixins: [intervalMixin],
  componentDidMount: function(){
    this.setInterval(this.forceUpdate.bind(this), 1000);
  },
  render: function(){
    var from = Number(new Date(2014, 1, 1));
    var to = Date.now();
    return (
      <div>2014/01/01からの経過秒数:{Math.round((to-from)/1000)}</div>
    );
  }
});