はじめに
Reduxを学習するにあたり、Redux公式サイトを翻訳しながら読んでみたので、それをつらつらと書いています。
基本は翻訳なので、英語が読める方には意味ないですが、抵抗がある方には良いのではないでしょうか。
Reduxとは?
Reduxは、「アクション」と呼ばれるイベントを使って、アプリケーションの状態を管理・更新するためのライブラリです。
アプリケーション全体で使用する必要のあるstateを保存し、予測可能な方法でのみstateが更新されるようにルールを設定します。
なぜReduxを使うべきなのか?
Reduxはグローバルな状態を管理するのに役立ちます。
Reduxが提供するパターンとツールにより、アプリケーション内のstateがいつ、どこで、なぜ、どのように更新されるのか、そしてその変更が発生したときにアプリケーションロジックがどのように動作するのかを容易に理解することができます。
Reduxは、予測可能でテスト可能なコードを書くことを可能にし、アプリケーションが期待通りに動作することを確認させてくれます。
どんなときにReduxを使うべきか?
・ アプリケーション内で多くの場所で必要とされる大量のstateがある
・ アプリの状態が時間の経過とともに頻繁に更新される状態を更新するロジックが複雑な場合
・ アプリのコードが中・大規模で、多くの人が作業する可能性がある
Redux Storeとは
Redux Storeとはアプリケーションのグローバルな状態を保持するコンテナのことです。
ストアはJavaScriptのオブジェクトで、普通のグローバルオブジェクトとは異なるいくつかの機能を持っています。
・ Reduxのストア内に保持されている状態を直接修正・更新してはいけません。
・ 代わりにステートの更新を引き起こす唯一の方法は、「アプリケーションで起こった何か」を記述するactionオブジェクトを作成し、そのactionをstoreにdispachして何が起こったかを伝えることです。
・ actionがdispatchされると、storeはrootReducer関数を実行し、古いstateとactionに基づいて新しいstateを計算させます。
・ 最後にstoreはsubscribersに状態が更新されたことを通知し、UIが新しいデータで更新されるようにします。
state, action, reducer
まず、アプリケーションを記述するための初期状態の値を定義することから始めます。
const initialState = {
value: 0
}
ここではカウンターの現在の値で1つの数値をトラッキングすることにします。
Reduxアプリは通常、stateのrootピースとしてJSオブジェクトを持ち、そのオブジェクトの中に他の値を持ちます。
次にreducer関数を定義します。reducerは2つの引数、現在の状態と、actionオブジェクトを受け取ります。Reduxアプリの起動時にはまだ何の状態もないので、reducerのデフォルト値として、initialStateを用意します。
const counterReducer = (state=initialState, action) => {
switch(action.type) {
case "counter/incremented":
return {...state, value: state.value + 1}
case "counter/decremented":
return {...state, value: state.value - 1}
default: return state
}
}
actionオブジェクトには必ずタイプフィールドがあります。これは、アクションのユニークな名前として機能する文字列です。
タイプは、このコードがみた人が意味を理解できるように読みやすい名前にします。
このケースでは、action.typeの前半に「counter」という単語を使用し、「後半に何が起こったか」を記述します。
このケースでは、「counter」が「increment」されたので、アクションタイプを「counter/increment」と記述しています。
action.typeに応じて、新しい状態の結果として新しいオブジェクトを返すか、何も変わらなければ既存のinitialStateオブジェクトを返す必要があります。
オリジナルのオブジェクトを直接更新するのではなく、既存の状態をコピーして更新することで、状態を不変に更新することに注意してください。
Store
reducer関数ができたので、ReduxライブラリのcreateStore APIを呼び出してストアインスタンスを作成します。
const store = Redux.createStore(counterReducer)
このreducer関数をcreateStoreに渡すと、createStoreはreducer関数を使って初期状態を生成し、将来のアップデートを計算します。
UI
ユーザが何かをすると、アプリはその状態を更新し、その値でUIを再描画します。
const valueEl = document.getElementById("value")
const render = () => {
const state = store.getState()
valueEl.innerHTML = state.value.toString()
}
render ()
store.subscribe(render)
store.getStateメソッドを使って、Reduxストアから最新の状態を取得する方法を知っている関数を書き、その値を取得してUIを更新して表示します。
ReduxStoreでは、store.subscribe()を呼び出してstoreが更新されるたびに呼び出されるsubscribeコールバック関数を渡すことができます。
つまり、subscriberとしてレンダー関数を渡せば、storeが更新されるたびに最新の値でUIを更新することができるのです。
Dispatch actions
最後に、何が起こったかを記述するactionオブジェクトを作成し、それをストアにdispatchすることで、ユーザの入力に応答する必要があります。
store.dispatch(action)を呼び出すと、ストアはreducerを実行し、更新された状態を計算し、UIを更新するためにsubscriberを実行します。
document.getElementById("increment").addEventListener("click", () => {
store.dispatch({type: "counter/incremented"})
})
document.getElementById("decrement").addEventListener("click", () => {
store.dispatch({type: "counter/decremented"})
})
document
.getElementById("incrementIfOdd")
.addEventListener("click", () => {
if(store.getStore().value % 2 !== 0) {
setTimeout(() => {
store.dispatch({type: "counter/incremented"})
}, 1000)
}
})
ここでは、reducerに現在のカウンタ値から1を加えたり、1を引いたりするactionをdispatchします。
またある条件が満たされた場合のみactionをdispatchするコードを書いたり、遅延後にactionをdispatchする非同期コードを書いたりすることもできます。
Data Flow
・ クリックなどのユーザインタラクションに応じてactionがdispatchされる
・ storeがReducer関数を実行して、新しい状態を計算する
・ UIが新しい状態を読み込んで新しい値を表示する
引用 (https://redux.js.org/tutorials/fundamentals/part-1-overview#data-flow)
まとめ
- Reduxはグローバルなアプリケーションの状態を管理するためのライブラリ
- ReduxはReduxとReactを結合するためのReact-Reduxライブラリと一緒に使うのが一般的です
- Redux ToolkitはReduxのロジックを書くのに推奨される方法です
- Reduxはいくつかのタイプのコードを使用します
- actionはtypeフィールドをもつオブジェクトで、アプリで何が起こったかを記述します
- reducerは以前の状態とactionに基づいて新しい状態の値を計算する関数です
- Redux Storeはactionがdispatchされるたびにrootのreducerを実行します。
