react-bootstrapを使ってみる(Input)

# 入力系のコンポーネントを使ってみよう
ボタンの時に一緒にやっておけって感じだけど、本人が勉強しながらなので順番があまりよろしくないです。あと、入力するだけでは芸がなにので簡単なチェック処理も入れてみる。

以下は完成時のイメージ。

5文字以下だとエラー(ついでにAlertコンポーネントにメッセージを表示)
![length5.PNG](https://qiita-image-store.s3.amazonaws.com/0/64288/9e683573-3165-ec2f-9c54-d848d57468b8.png)

10文字以下だと警告
![length10.PNG](https://qiita-image-store.s3.amazonaws.com/0/64288/f3b6ff29-a351-75bf-0874-44479698a887.png)

11文字以上だとOK
![length11.PNG](https://qiita-image-store.s3.amazonaws.com/0/64288/58ca8087-f2bd-9d09-54ba-46db1337c092.png)

# コンポーネントの作成
InputSampleという芸のないコンポーネント名でビューの処理を書く。

```js
// InputSample.jsx
import Arda from 'arda';
import {Input, Alert} from 'react-bootstrap';

export default class InputSample extends Arda.Component {
    render() {
       return (
            <div>
                { /* Inputコンポーネント */ }
                <Input 
                    type="text" 
                    value={this.props.value /* 表示内容 */ } 
                    placeholder="Enter text" 
                    label="Working example with validate" 
                    help="Validates bsed on string length." 
                    bsStyle={this.getStyle() /* 'success'とか'error'を指定するとそれっぽい見た目に */ } 
                    hasFeedback 
                    ref="input" 
                    onChange={(e)=>this.onChange(e) /* 文字を入力する毎にイベント発生 */ } />

                { /* メッセージがあればAlertコンポーネントを表示する */}
                { this.getAlert() }
           </div>
        );
    }

    // 入力値(value)と検証結果(validation)によって見た目(スタイル)を変える
    getStyle() {
        var v = this.props.validation;

        // 検証結果がOK
        if (v.valid) {
            // OKだけど長さが10以下なら警告を出す
            let len = this.props.value.length;
            if (len <= 10)
                return "warning";
            return "success";
        }
       return "error";
    }
 
    // 入力値の検証メッセージがあればAlertコンポーネントに表示する
    getAlert() {
        let msg = "";
        let elem = null;
        let v = this.props.validation; 
        if (!v.valid) {
            msg = v.errors.map(error=>error.message).join();
            elem = <Alert bsStyle="danger"> {msg} </Alert>;
        }

        return elem;
    }

   onChange(e) {
        e.preventDefault();

        // 入力があったことを通知(値も一緒に)
        let val = this.refs.input.getValue(); 
        this.dispatch('InputSample:changed', val);
    }
}
```

コンポーネントはビューを担当し、値(props.value)と検証結果(props.validation)を元に表示内容を用意するだけ。入力イベントもdispatchメソッドを使ってコンテキストへ投げるだけでロジックは持たない。

# コンテキストの作成
次に、コンテキスト及び、InputSampleコンポーネントの呼び出し元。

```js
// App.jsx
window.React = require('react');
window.Promise = require('bluebird');
import Arda from 'arda';
import ZSchema from 'z-schema';
import InputSample from './InputSample.jsx';

class App extends Arda.Component {
    render() {
        return (
                <div>
                    { /* ...はspread operatorというやつ。こう書くとpropsのプロパティがすべてInputSampleに渡される */ }
                    <InputSample {...this.props} />
                </div>
        );
    }
}

class AppContext extends Arda.Context {
    static get component() { return App; }

    constructor(component, props) {
        super(component, props);

        // JSON Schema validator
        this.validator = new ZSchema();

        // value用のスキーマ定義
        this.valueSchema = {
            type: 'string',
            minLength:6
       };
    }

    initState() { 
        let value = '';
        
       return { 
           value,
           validation: this.validate(value)
       }; 
    }

    expandComponentProps(props, state) { 
        return { 
            value: state.value,
            validation: state.validation
        };
    }

    // 入力値を検証する
    validate(value) {
        return {
            valid: this.validator.validate(value, this.valueSchema),
            errors: this.validator.getLastErrors()
        };
    }

    delegate(subscribe) {
        super.delegate();

        // 入力イベントを受け取る
        subscribe('InputSample:changed', (value)=> {
            this.update(()=> {
                return {
                    value,
                    validation: this.validate(value)
                };
            });
        });
    }
}

window.addEventListener('DOMContentLoaded', ()=> {
    let router = new Arda.Router(Arda.DefaultLayout, document.getElementById('container'));
    router.pushContext(AppContext, {});
});
```

コンテキストは状態としてvalueとその検証結果であるvalidateを持つ。検証にはJSON Schemaを使うことにし、次のように定義する。

```js
// value用のスキーマ定義
this.valueSchema = {
    type: 'string',
    minLength:6
};
```

ここでは、valueの型は'string'で最小長は6とした。JSON Schemaの詳細については以下を参照のこと。

[JSON Schema and Hyper-Schema](http://json-schema.org/)

スキーマを定義したら、その定義の違反していないか検証したい。[ここ](http://json-schema.org/implementations.html)のValidatorsを見ると検証用ライブラリがたくさんある。今回は[z-schema](https://github.com/zaggino/z-schema)を使うことにした。~~~理由は更新日が3日前で、なんかメンテされてそうだったというだけ。~~~

では、インストール。

```
> npm install z-schema
```

使い方は

```js

// JSON Schema validator
this.validator = new ZSchema();

let validation = this.validator.validate(値, スキーマ);

```

こんな感じ。

道具の説明は終わったので、いつも通りsubscribeでイベントを受け取って、入力値とその検証結果を新たな状態として更新してやる。

コードはこの部分。

```js
// 入力イベントを受け取る
        subscribe('InputSample:changed', (value)=> {
            this.update(()=> {
                return {
                    value,
                    validation: this.validate(value)
                };
            });
        });
```

するとInputSampleコンポーネントに通知が飛び、描画される。

コンテキスト側については、サンプルコードなので状態も少なくあっさりだけど、規模が大きくなったら状態及びスキーマ定義をちゃんと設計しないと酷いことになりそ。