reselectを用いてReact Reduxにvalidationの仕組みを実装する 2/2

  • 9
    Like
  • 0
    Comment

本記事はSupership株式会社 Advent Calendar 2016の8日目の記事になります。

株式会社Socket@notsunohito です。
株式会社SocketはSupership株式会社と同じSyn.グループのメンバーであり
Web接客と呼ばれるサービスのひとつであるFlipdeskを展開しています。

昨日の7日目はreselectでvalidationを行うselectorを定義する所まで進みました。

本日8日目はそのselectorでstateをvalidationしその結果を値化したValidationStatus
React Reduxのpropsに組み込んでViewで表示する所まで行います。

TL;DR

reduxmapStateToPropsの中でrootSelectorで呼び出してpropsとして
アプリケーションのrootコンポーネントに注入しあとは好きに使う。

本記事で扱うサンプル全体のソースコードは
notsunohito/reselect-validation-exampleで、
実際の動作はここで確認できます。

mapStateToProps内でrootSelectorで呼び出す

7日目のselectorのspecにある通りselectorはselector(state)のようにして使います。
React Reduxで使うときは基本的に公式にある通りmapStateToProps内で呼び出してあげます。

container/app.js

container/app.js
function mapStateToProps(state) {
    const validation = rootSelector(state)
    return {
        state,
        validation
    }
}

これでRootコンポーネントからvalidationの結果をprops.validationとして扱えるようになりました。

Rootコンポーネントのprops経由で各コンポーネントに渡す


あとはprops.validationを他のpropsと同様にコンポーネント内で好きに使うだけです。
完成したサンプルはこちら

*1 ValidationIconvalidationStatusを表示するコンポーネントです。
*2 ValidationStatusERRORのときはエラーであることを表すアイコンを表示します。
*3 ERRORmessageが設定されていればそれをTooltipとして表示します。
*4 OKのときはOKであること表すアイコンを表示します。

components/MainPanel.js

components/MainPanel.js
const React = require('react')
const {validationStatus} = require('../constants')
const {isOK, isERROR} = validationStatus


function MainPanel(props) {
    const {state, validation, actions} = props;
    return (
        <div id="main_panel" className="pure-form">
            <div id="title">
                <h1>Company</h1>
                <ValidationIcon status={validation.summary} />
            </div>
            <div className="item flex-box">
                <Label>Name</Label>
                <TextInput value={state.name} onChange={actions.changeName} />
                <ValidationIcon status={validation.name} />
            </div>
            <div className="item flex-box">
                <Label>ZipCode</Label>
                <TextInput value={state.zipCode} onChange={actions.changeZipCode} />
                <ValidationIcon status={validation.zipCode} />
            </div>
            <div className="item flex-box">
                <Label>Address</Label>
                <TextInput value={state.address} onChange={actions.changeAddress} />
                <ValidationIcon status={validation.address} />
            </div>
            <div className="flex-box">
                <button id="clear_button" className="pure-button" onClick={actions.clearAll}>Clear</button>
            </div>
        </div>
    )
}

function ValidationIcon(props) { // *1
    const {status} = props;
    if(isERROR(status)) { // *2
        return (
            <div className="validation-icon">
                <span className='glyphicon glyphicon-remove'></span>
                {status.hasMessage
                    ? <ToolTip message={status.message} /> // *3
                    : ''}
            </div>
        )
    }
    if(isOK(status)) { // *4
        return (
            <div className="validation-icon">
               <span className='validation-icon glyphicon glyphicon-ok'></span>
            </div>
        )
    }
    throw new Error(`No such validation status: ${status.type}`)
}

function ToolTip(props) {
    const {message} = props;
    return (
        <div>
            <div className='message-tooltip-triangle'></div>
            <div className='message-tooltip'>{message}</div>
        </div>
    )
}


function Label(props) {
    const {children} = props
    return (<label className="fixed">{children}</label>)
}

function TextInput(props) {
    const {value, onChange} = props;
    return (
        <input
            type='text'
            value={value}
            onChange={(e)=>onChange(e.target.value)}
            className="text-input"
        />
    )
}


module.exports = MainPanel

まとめ

reselectを使うとredux wayを崩ずつことなくvalidationを実現できることがわかると思います。

ただし、今回のサンプルではvalidationに応じた入力制限等を行っていません。
さらには、非同期な処理の考慮もしていませんが仕組みが単純なので工夫さえ施せば実現は難しくないと考えています。

reselectは当然validation以外にもcomputedな値を計算するにはどのような用途でも使えます。
computedなことを扱うなら必要になるライブラリなのでvalidationもcomputedとして扱うのが一番だと考えています。

以上二日間に渡りreselectに関して書かせて頂きました。最後まで読んで頂きありがとうございます。

弊社では自社で開発・運用しているサービスFlipdeskを共に成長させるメンバーを探しています。
興味ある方はwantedlyをご一読頂ければと思います。

明日 9日目の Supership株式会社 Advent Calendar 2016 は @endout さんです。
よろしくお願いします!