Reactとセットで使われることの多いReduxについてちゃんと把握したかったので、公式ページに書かれている内容をもとに自分なりに説明してみる。
(サンプルコードは公式ページからコピーさせていただきました)
コアコンセプト
アプリケーションのステータスがオブジェクト形式で表現された場合をイメージしてみましょう。
例えば、TODOアプリケーションのステータスを表現する場合は以下の通りになります。
{
todos: [{
text: 'なにか食べる',
completed: true
}, {
text: 'エクササイズをする',
completed: false
}],
visibilityFilter: 'SHOW_COMPLETED'
}
このオブジェクトは、Setterがないことを除けば、"モデル"のように見えます。
この形式だと、コードの様々な部分の変更が難しくなり、バグの再現が難しくなります※
状態に変更を加えるためにアクションを別管理する必要があります。
アクションは何が起こったかを説明するためのプレーンなJavaScriptオブジェクトです。
以下のコードがアクションのサンプルです。
{ type: 'ADD_TODO', text: 'プールに行く' }
{ type: 'TOGGLE_TODO', index: 1 }
{ type: 'SET_VISIBILITY_FILTER', filter: 'SHOW_ALL' }
すべてのアクションがアクションとして表現されているかを確認するために、アプリケーションの内部で何が起こっているかを明確にします。
もし、何かに変化があれば、なぜ変更があったかがわかります。
アクションは何が起こったかのパンくずリストのようなものです。
最終的に、状態とアクションを一緒にするために、reducerと呼ばれる関数を作成します。
何も不思議なことはなく、これは状態とアクションを引数として受け取り、戻り値に次の状態を返すただの関数です。
大きなアプリケーションでこのような関数を記述するのはちょっと大変です。そのため、パーツの状態を管理するための小さな関数を作りました。
function visibilityFilter(state = 'SHOW_ALL', action) {
if (action.type === 'SET_VISIBILITY_FILTER') {
return action.filter
} else {
return state
}
}
function todos(state = [], action) {
switch (action.type) {
case 'ADD_TODO':
return state.concat([{ text: action.text, completed: false }])
case 'TOGGLE_TODO':
return state.map((todo, index) =>
action.index === index
? { text: todo.text, completed: !todo.completed }
: todo
)
default:
return state
}
}
対応する状態キーにより2つのreducerを呼び出し、
アプリケーションの状態を全体的に管理するための別のreducerを作りました。
function todoApp(state = {}, action) {
return {
todos: todos(state.todos, action),
visibilityFilter: visibilityFilter(state.visibilityFilter, action)
}
}
これが基本的にReduxの考え方のすべてです。
現時点では、ReduxのAPIを全く使っていないことに着目してください。
このパターンを使う場合、いくつかのユーティリティが付属していますが、
主となるアイデアとしては、どのように状態(state)が更新されたかをアクションオブジェクトに応じて説明することです。
また90%のあなたが記述するコードがプレーンなJavascriptで、ReduxやそのAPIを利用することはありません。