React.jsのProp

今回は前回にも少し登場したPropについて取り上げたいと思います。

## 基本的な使い方

Propは基本的にはCompnentのattributeとして定義してComponentの中では`this.props.xxx`として参照する。それだけです。PropにはObjectでも関数でも何でも指定することが出来ます。

```js
var Avatar = React.createClass({
  render() {
    var avatarImg = `/img/avatar_${this.props.user.id}.png`;
    return(
      <div>
        <span>{this.props.user.name}</span>
        <img src={avatarImg} />
      </div>
    );
  }
});

var user = {
  id: 10,
  name: "Hoge"
};

// <Avatar user={user} />
```


## I/FとしてのProp

Propは外部から渡された値でそのComponentが管理している値ではないので変更してはいけません。
そのComponentが変更を管理するような値の場合は明日に紹介するStateにするべきです。

つまりPropはImmutableであり外部とのI/Fとなっています。


## PropTypes

PropはComponentの外部I/FとなるものでありそうなってくるとValidationしたくなります。
そこでReact.jsではPropTypesとしてPropについての型などの制約を指定することが出来ます。

地味にいい機能ですね。

```
var Avatar = React.createClass({
  propTypes: {
    name:   React.PropTypes.string.isRequired,
    id:     React.PropTypes.number.isRequired,
    width:  React.PropTypes.number.isRequired,
    height: React.PropTypes.number.isRequired,
    alt:    React.PropTypes.string
  },
  render() {
    var src = `/img/avatar/${this.props.id}.png`;
    return (
      <div>
        <img src={src} width={this.props.width} height={this.props.height} alt={this.props.alt} />
        <span>{this.props.name}</span>
      </div>
    );
  }
});

<Avatar name="foo" id=1 width=100 height=100 />
```

こんな感じで書くことができるので、PropTypeを指定することでそのComponentのI/Fを明確に示すことが出来るようになります。

PropTypesの指定はこんな感じの指定が可能なのでかなり柔軟に指定することが出来ます。

```
React.PropTypes.array           // 配列
React.PropTypes.bool.isRequired // Booleanで必須
React.PropTypes.func            // 関数
React.PropTypes.number          // 数値
React.PropTypes.object          // オブジェクト
React.PropTypes.string          // 文字列
React.PropTypes.node            // Renderできるもの
React.PropTypes.element         // React Element
React.PropTypes.instanceOf(XXX) // XXXのinstanceかどうか
React.PropTypes.oneOf(['foo', 'bar']) // fooかbar
React.PropTypes.oneOfType([React.PropTypes.string, React.PropTypes.array]) // 文字列か配列
React.PropTypes.arrayOf(React.PropTypes.string)  // 文字列の配列かどうか
React.PropTypes.objectOf(React.PropTypes.string) // 文字列の値を持っているか
React.PropTypes.shape({                          // 指定された形式を満たしているかどうか
  color: React.PropTypes.string,
  fontSize: React.PropTypes.number
});
React.PropTypes.any.isRequired  // なんでもいいけど必須

// カスタムの制約も定義出来る(ダメな場合はError投げる)
customPropType: function(props, propName, componentName) {
  if (!/^[0-9]/.test(props[propName])) {
    return new Error('Validation failed!');
  }
}
```

一番最初に紹介したコードにPropTypesを定義するとこんな感じです。

```js
var Avatar = React.createClass({
  propTypes: {
    user: React.PropTypes.shape({
      id:   React.PropTypes.number.isRequired,
      name: React.PropTypes.string.isRequired
    })
  },
  render() {
    var avatarImg = `/img/avatar_${this.props.user.id}.png`;
    return(
      <div>
        <span>{this.props.user.name}</span>
        <img src={avatarImg} />
      </div>
    );
  }
});
```

注意点としては、ここで指定した制約については、パフォーマンス的な理由からProduction環境ではチェックされずに、Developでもエラーになるわけではなくて`console.warn`で出力されるのみです。

エラーにしてほしいというissueも上がってたりしたので今後エラーになるなどの変更もあるかもですが...。

## デフォルト値の指定

getDefaultPropsでObjectを返すことでデフォルト値の指定も可能です。

これはComponentのインスタンスが作られる時ではなく、Componentの定義が作成されるときだけに呼ばれるのでその点注意が必要です。(明日紹介する`getInitialState`とは違う)

```js
var Hello = React.createClass({
  getDefaultProps() {
    return {
      name: "React"
    };  
  },
  render() {
    return <div>Hello {this.props.name}</div>
  }
});

// <Hello />
```

## setProps & replaceProps

Componentに新しいPropを渡して再度rerenderしたいような場合、setPropsとreplacePropsを使うことでPropを更新しつつrerenderすることが出来ます。

```js
var Test = React.createClass({
  getDefaultProps: function() {
    return {
      id: 1
    };
  },
  render: function() {
    return (
      <div>{this.props.id}:{this.props.name}</div>
    );
  }
});

var component = React.render(<Test name="bar" />, document.body);

component.setProps({ name: "foo" });      // <div>1:foo</div>
component.replaceProps({ name: "hoge" }); // <div>:hoge</div>
```

setPropsとreplacePropsの違いは、Propの値をmergeするか置き換えるかです。
また、それぞれ第二引数にcallback関数を指定することも出来ます。


------------

というわけで、Propの紹介でした。

明日はStateについて紹介したいと思います。