React.jsのmixinについて

今回はComponentのmixinについて書きたいと思います。

mixinはまさにmixinなので概念自体については説明しませんがComponentの共通な振る舞いをObjectとして共通化することが出来てとても便利です。

React.js自体も、LinkedStateMixinやPureRenderMixinといった形でmixinを提供しています.

ちなみにMarionette.jsでもBehaviorとして、Vue.jsでもmixinとして存在しています。

## 使い方

使い方はmixinsにObjectを配列として指定するだけです。
配列である通り複数指定することも出来ます。

```js
var Logger = {
  logging(str) {
    console.log(str);
  },
  componentDidMount() {
    this.logging("component did mount");
  }
};

var Hello = React.createClass({
  mixins: [Logger],
  render() {
    this.logging("render");
    return <div>Hello</div>
  }
});
```

## mixinが読み込まれる順番

複数mixinを指定出来るということでどのような順番で読まれるのか確認してみます。

```js
var MixinA = {
  componentWillMount() {
    console.log("mixinA");
  }
};

var MixinB = {
  componentWillMount() {
    console.log("mixinB");
  }
};

var Hello = React.createClass({
  mixins: [MixinA, MixinB],
  componentWillMount() {
    console.log("hello");
  },
  render() {
    return <div>hello</div>
  }
});

React.render(<Hello />, document.body);
// mixinA
// mixinB
// hello
```

というわけで予想通り、配列の順番で呼ばれて最後にComponentのものが呼ばれるのがわかります。

## Conflict State or Prop

ところで、`getInitialState`や`getDefaultProps`などをmixinで指定するとどうなるのでしょうか。

見てみます。

### getInitialState

```js
var Mixin = {
  getInitialState() {
    return {
      mixinValue: "mixin state"
    };
  }
};

var Hello = React.createClass({
  mixins: [Mixin],
  getInitialState() {
    return {
      componentValue: "component state"
    };
  },
  render() {
    console.log(this.state);
    return <div>hello</div>
  }
});

React.render(<Hello />, document.body);
//  Object {mixinValue: "mixin state", componentValue: "component state"}
```

というわけで値をmergeしてくれます。

### getDefaultProps

```js

var Mixin = {
  getDefaultProps: function() {
    return {
      mixinValue: "mixin prop"
    };
  }
};

var Hello = React.createClass({
  mixins: [Mixin],
  getDefaultProps: function() {
    return {
      componentValue: "component prop"    
    };
  },
  render: function() {
    console.log(this.props);
    return <div>hello</div>
  }
});

React.render(<Hello />, document.body);
 Object {mixinValue: "mixin prop", componentValue: "component prop"}
```

こちらも値をmergeしてくれます。

### getInitialStateで同じkeyを指定する

```js
var Mixin = {
  getInitialState() {
    return {
      value: "mixin state"
    };
  }
};

var Hello = React.createClass({
  mixins: [Mixin],
  getInitialState() {
    return {
      value: "component state"
    };
  },
  render() {
    console.log(this.state);
    return <div>hello</div>
  }
});

React.render(<Hello />, document.body);
//  Uncaught Error: Invariant Violation: mergeObjectsWithNoDuplicateKeys(): Tried to merge two objects with the same key: `value`. This conflict may be due to a mixin; in particular, this may be caused by two getInitialState() or getDefaultProps() methods returning objects with clashing keys.
```

というわけで同じkeyの値を指定するとエラーになってしまうので注意が必要です。

## 二重定義

同じメソッドをmixinとComponentで定義してみます。

```js
var Mixin = {
  foo: function() {
    console.log("mixin foo");
  }
};

var Hello = React.createClass({
  mixins: [Mixin],
  foo: function() {
    console.log("component foo");
  },
  render: function() {
    return <div>hello</div>
  }
});

React.render(<Hello />, document.body);
// Uncaught Error: Invariant Violation: ReactCompositeComponentInterface: You are attempting to define `foo` on your component more than once. This conflict may be due to a mixin.
```

この場合はエラーになります。

---------

mixinをうまく使うことでコードを減らすことが出来るので、わかりにくくなりすぎない程度に使っていくと便利です。

今回はmixinについて書きました。
明日は、Addonについて書きたいと思います。