Reduxの用語と概要
はじめに
Reduxの公式ドキュメントに沿って概要を押さえつつ、気になった部分を深掘って整理しています。
本記事では 、Redux用語についてまとめていきます。
Actions
Reduxにおけるアクションは、アプリケーションで何が起こったかを記述するイベントとして考えることができます。これは単なるJavaScriptのオブジェクトで、typeフィールドを持ちます。
下記は一般的なアクションのオブジェクトです。
const addTodoAction = { type: 'todos/todoAdded', payload: 'Buy milk' }
type
アクションに名前を付けるための文字列で、上記の例では"todos/todoAdded"のような形式を取っています。
この文字列は"domain/eventName"の形式で書かれ、最初の部分がこのアクションが所属する機能やカテゴリーを表し、2つ目の部分は具体的に何が起こったかを表します。
payload
アクションオブジェクトは、何が起こったかについての追加情報を持つ他のフィールドも持つことができます。
慣例により、その情報はpayloadというフィールドに格納します。
Action Creators
アクションオブジェクトを返す関数のことです。
アクションオブジェクトを毎回手動で書かなくてもいいように、一般的にはこちらが使用されます。
Reducers
ReduxでのReducer
は、アプリケーションの状態を変更するための純粋な関数です。
現在の状態とアクションを引数に取り、必要に応じてステートを更新する処理を行い、新しい状態を返します。
function reducer(state, action) { // 新しい状態を返す }
Reducersのルール
- 責務の分離 ステートとアクションの引数に基づいて、新しいステート値を計算するだけの責務であるべきです。
- イミュータビリティ 状態を直接変更するのではなく、既存の状態をコピーし、コピーされた値に変更を加えることで、不変性を保つ必要があります。
- Pure function 同じ入力(状態とアクション)が与えられた場合、外部の状態や値に依存しない、常に同じ出力を生成する必要があります。
ReduxのReducer
の一部として機能する関数counterReducer
を定義しています。
counterReducer
関数は、action.type
が'counter/increment'である場合にのみ状態を更新します。
それ以外のアクションが与えられた場合には、現在の状態をそのまま返します(状態を変更しません)。
// 初期状態を定義します const initialState = { value: 0 } // `counterReducer`関数を定義します。これはstateとactionを引数に取ります function counterReducer(state = initialState, action) { // このreducerがactionに対応しているか確認します if (action.type === 'counter/increment') { // 対応している場合、stateのコピーを作ります return { ...state, // そしてコピーの値を新しい値で更新します value: state.value + 1 } } // それ以外の場合、元のstateを変更せずにそのまま返します return state }
また、新しい状態を生成する際には、既存の状態オブジェクトのコピーを作成し、そのvalue
フィールドの値を増分します。
これにより、新しい状態オブジェクトが生成され、前の状態オブジェクトは不変性を保持します。このように、Reduxでは状態の不変性が重要となります。
Store
Redux アプリケーションの現在の状態は、ストアと呼ばれるオブジェクト内に存在します。
@reduxjs/toolkit
からconfigureStore
関数をインポートして、新しいReduxストアを作成しています。
この関数は、アプリケーションの状態を保存し、アクションをディスパッチすることでその状態を更新する能力を持つストアオブジェクトを作成します。
ストアはリデューサーを引数に渡すことによってインスタンス化されます。
getState
は現在の状態値を返すメソッドです。
import { configureStore } from '@reduxjs/toolkit' const store = configureStore({ reducer: counterReducer }) console.log(store.getState()) // {value: 0}
dispatch
Storeはdispatchというメソッドを持ちます。
reduxでは、store.dispatch()
アクション オブジェクトを呼び出して渡すことで状態を更新するのが基本です。
下記の例では、先程カウントするreducerを引数にインスタンス化したストアオブジェクトでdispatchを呼び出しています。 引数にアクションオブジェクトを渡すことで、reducerでは状態を更新する処理が実行されています。
store.dispatch({ type: 'counter/increment' }) console.log(store.getState()) // {value: 1}
アクションのディスパッチは、アプリケーションでの「イベントのトリガー」と考えることができます。何かが起こったので、ストアにそれについて知らせてほしいと考えています。
Reducer はイベントリスナーのように動作し、アクションに応じて状態を更新します。
下記のようにアクションオブジェクトを渡しても良いです。
const increment = () => { return { type: 'counter/increment' } } store.dispatch(increment()) console.log(store.getState()) // {value: 2}
Selectors
ストア状態値から特定の情報を抽出する方法を認識する関数です。
下記のコードでは、selectCounterValue
というセレクターを定義しています。これは状態オブジェクトを引数に取り、そのvalue
プロパティを返す関数です。この関数を使って、ストアの現在の状態からvalue
プロパティを抽出することができます。
const selectCounterValue = state => state.value const currentValue = selectCounterValue(store.getState()) console.log(currentValue) // 2
セレクターを使用すると、状態から必要なデータを簡単に取り出すことができ、コードの可読性や保守性が向上します。
##### データの整形ができる
状態の形状が複雑である場合や、特定の形状でデータを必要とするコンポーネントがある場合、セレクターを使用して状態を適切な形状に整形することができます。
これにより、コンポーネント自体は整形されたデータを受け取り、その内部でデータを再加工する必要がなくなります。
データの再利用
同じ形状のデータが複数の場所で必要とされる場合、セレクターを使ってそのロジックを一箇所にまとめることができます。
これにより、コードの重複を避けると同時に、データの取得方法が変更された場合でも修正が一箇所で済むようになります。
参考
- モダンJavaScriptの基本から始める React実践の教科書
- https://redux.js.org/tutorials/essentials/part-1-overview-concepts
続く…
コメント
本記事の内容は以上になります!
書籍の続きのアウトプットも随時更新したいと思います。
プログラミングスクールのご紹介 (卒業生より)
お世話になったプログラミングスクールであるRUNTEQです♪
こちらのリンクを経由すると1万円引きになります。
RUNTEQを通じて開発学習の末、受託開発企業をご紹介いただき、現在も双方とご縁があります。
もし、興味がありましたらお気軽にコメントか、TwitterのDMでお声掛けください。