Reactでは、stateを使用することで状態管理を行なっていました。
ただ、stateはそのコンポーネントでしかアクセスできませんでした。
つまり、他のコンポーネントからアクセスができず、同じstateを使用したくても難しいようでした。
これを解決するのがReduxのようです。
※コンポーネント : UIの一部を構成する部品であり、再利用性と保守性を高めるために作成されます。Reactコンポーネントは、JavaScriptのクラスまたは関数として定義されることが一般的です。
Reduxの目的
- 状態の一貫性: アプリケーションの状態は、アプリケーション内のさまざまなコンポーネントによって変更されることがあります。これにより、状態の一貫性を保つことが難しくなる場合があります。Reduxでは、状態を一元的に管理し、直接変更することを避けることで、状態の一貫性を確保します。
- 可予測な状態変更: Reduxは、状態の変更を「アクション」と呼ばれるオブジェクトとして表現します。これにより、状態変更を事前に予測可能な方法で実行できます。アクションは、アプリケーション内のどこからでもディスパッチ(送信)されることができます。
- アプリケーションのデバッグとテスト: Reduxを使用すると、アプリケーションの状態変更を追跡し、デバッグすることが容易になります。また、状態の変更に対するテストも簡単に行うことができます。
特に、
Reduxのストア(Store)は、Reactコンポーネントの外部にあり、アプリケーション全体で共有される状態を保持します。このストアによって、アプリケーション内のどのコンポーネントからでも状態にアクセスできるようになるようです。
さらに、アクション(Action)は、Storeの値の変更ができるようです。
また、ディスパッチ(Dispatch)を使用することで、アクションで変更したStoreの値を送信できるようです。
送信されたものは、リデュース(Reduce)と呼ばれるところへ送られて新しい状態としてStoreへ変更を実行・反映します。
Reduxの構成
Reduxは下記のような構成で成り立っているようです。
- Store(ストア): アプリケーション全体の状態を保持するオブジェクトです。アプリケーション内の状態は、このストアによって管理されます。
- Actions(アクション): 状態の変更を記述するオブジェクトです。アクションは、typeプロパティを持ち、状態の変更の種類を表します。
- Reducers(リデューサー): 現在の状態とアクションを受け取り、新しい状態を生成する純粋な関数です。Reducersは、ストア内で状態の変更を実際に処理します。
- Dispatcher(ディスパッチャー): アクションをReducersに送信するためのメソッドです。アプリケーション内のどこからでもアクションをディスパッチすることができます。
Reduxのストアを作成する
Reduxのストアの作成方法として
Redux.createStore() を使用します。
例として、下記のような課題がありました。
Redux の
store
は、アプリケーションのstate
を保持して管理するオブジェクトです。 Redux オブジェクトにcreateStore()
というメソッドがあり、Reduxstore
を作成するために使用します。 このメソッドは、必須の引数としてreducer
関数を受け取ります。reducer
関数についてはこの後のチャレンジで説明します。コードエディターではすでに定義が済んでいて、 単にstate
を引数に取り、state
を返します。https://www.freecodecamp.org/japanese/learn/front-end-development-libraries/redux/create-a-redux-store
store
変数を宣言し、それをcreateStore()
メソッドに割り当て、引数としてreducer
を渡してください。
下記のような解答になりました。
となりました。
上記のコードの中で、
const reducer = (state = 5) => {...}
の部分ですが、デフォルト引数というものでした。
デフォルト引数 : 関数が呼び出される際に引数が指定されなかった場合、デフォルト値が使用される。
これで、Storeが作成されました。
Redux ストアから state を取得する
Reduxストからstateを取得するには、
getState() メソッドを使用するようです。
下記のような課題がありました。
コードエディターで、前のチャレンジのコードが、より簡潔な内容に書き換えられています。
https://www.freecodecamp.org/japanese/learn/front-end-development-libraries/redux/get-state-from-the-redux-storestore.getState()
を使用して、store
からstate
を取得し、それを新しい変数currentState
に割り当ててください。
下記のような解答になりました。
Redux のアクションを定義する
Reduxの状態を変更するを記述するオブジェクトを作成します。
Reduxでのアクションを定義する方法は、type プロパティを持つオブジェクトを宣言するだけのようです。
const action = {type : 'LOGIN'}
となります。
アクションクリエイターを定義する
アクションを作成後、次のステップとして、Reduxストアにアクションを送信して、状態を更新できるようにする必要があるようです。
この処理を実行するのに使われるのが、アクションクリエイターだそうです。
アクションクリエイターは、単純にアクションを返すJavaScript関数だそうです。
別の言い方をすれば、アクションクリエイターは、アクションイベントを表すオブジェクトを作成するそうです。
下記のような課題がありました。
呼び出されたときに
https://www.freecodecamp.org/japanese/learn/front-end-development-libraries/redux/define-an-action-creatoraction
オブジェクトを返すactionCreator()
という関数を定義してください。
解答としては、下記のようになりました。
function actionCreator() {
return action;
}
のように、ただ関数を定義してオブジェクトを返すだけのようです。
ToDoリストでアクションを定義するとこのような感じになりました。
// ToDoを追加するアクション
const ADD_TODO = 'ADD_TODO';
const addTodoAction = (text) => ({
type: ADD_TODO,
payload: {
text: text,
completed: false
}
});
// ToDoを完了状態にするアクション
const TOGGLE_TODO = 'TOGGLE_TODO';
const toggleTodoAction = (id) => ({
type: TOGGLE_TODO,
payload: {
id: id
}
});
// ToDoを削除するアクション
const DELETE_TODO = 'DELETE_TODO';
const deleteTodoAction = (id) => ({
type: DELETE_TODO,
payload: {
id: id
}
});
上記の例では、3つのアクションを定義しました:
ADD_TODO
: 新しいToDoを追加するアクション。payload
プロパティには新しいToDoの内容が含まれます。TOGGLE_TODO
: ToDoの完了状態を切り替えるアクション。payload
プロパティには対象のToDoのIDが含まれます。DELETE_TODO
: ToDoを削除するアクション。payload
プロパティには対象のToDoのIDが含まれます。
アクションイベントをディスパッチする
アクションをReduxストアに送信することをディスパッチと呼ぶそうです。
どういう時に、ディスパッチを使用するのか調べてみたところ
具体的には、Reduxのアプリケーションで何かしらのイベントが発生した場合に、それに対応するアクションをディスパッチ(送信)することで、ストア内の状態を変更したり、特定の処理を実行したりするようです。
store.dispatch(action);
ここで、store
はReduxのストアオブジェクトであり、action
はディスパッチするアクションオブジェクトです。
下記のような課題がありました。
コードエディターの Redux ストアに初期化済みの state があります。これは
https://www.freecodecamp.org/japanese/learn/front-end-development-libraries/redux/dispatch-an-action-eventlogin
プロパティを含んでいるオブジェクトで、プロパティは現在false
に設定されています。 また、loginAction()
というアクションクリエイターがあり、これはタイプLOGIN
のアクションを返します。dispatch
メソッドを呼び出してLOGIN
アクションを Redux ストアにディスパッチし、loginAction()
によって作成されたアクションを渡してください。
解答は下記のようになりました。
ToDoリストのアプリケーションで新しいToDoを追加するアクションをディスパッチする場合を考えてみました。
// Reduxのストアからディスパッチする場合
store.dispatch({
type: 'ADD_TODO',
payload: {
text: '新しいToDo',
completed: false
}
});
ストアでアクションを処理する
アクションをディスパッチ(送信)した後、Reduxストアは送信されたアクションに対して、どう反応するか応答する必要があるようです。
その役割を行うのがreducer(リデューサー)関数だそうです。
Reduxのリデューサーは、アクションに応じてstateの変更を行うようです。
色々調べてややこしい印象しかなかったのですが、これだけ覚えておけば大丈夫な感じでした。
reducerは、state と action を受け取って新しい state を返すだけの純粋な関数である。
あと、もう一つ重要なことがありました。
Redux のもう一つの重要な原則は「state
は読み取り専用である」ことです。 言い換えれば、reducer
関数は常に state
の新しいコピーを返す必要があり、state を直接変更することは決して許されません。
下記のような課題がありました。
コードエディターに、以前に紹介した例と、
https://www.freecodecamp.org/japanese/learn/front-end-development-libraries/redux/handle-an-action-in-the-storereducer
関数の冒頭部分があります。reducer
関数の本体を入力して、タイプ'LOGIN'
のアクションを受け取った場合にlogin
がtrue
に設定された state オブジェクトを返すようにしてください。 それ以外の場合は、現在のstate
を返してください。 現在のstate
とディスパッチされたaction
がレデューサーに渡されるため、action.type
を使用してアクションタイプに直接アクセスすることができます。
解答としては下記のようになりました。
stateの変更をするときに、setState()を使用するのかと一瞬思いましたが、Reduxでstateの変更をかける時には、reducer()を使用するようです。
これには、下記のような利点があるようです。
Reducerは古いStateとアクションを受け取り、新しいStateを返す必要があります。ただし、Reducerは直接Stateを変更するのではなく、新しいStateオブジェクトを作成して返します。
これにより、Reduxは状態の変更を予測可能でトラッキングしやすいものとし、アプリケーション全体のデータフローが一元管理されることになります。また、Reducerが純粋な関数であるため、テストやデバッグが容易になります。
ToDoListアプリケーションではreducerを使用するとどのような感じになるか、
const initialState = {
todos: []
};
const todoReducer = (state = initialState, action) => {
switch (action.type) {
case 'ADD_TODO':
return {
...state,
todos: [...state.todos, action.payload]
};
case 'TOGGLE_TODO':
return {
...state,
todos: state.todos.map(todo =>
todo.id === action.payload.id ? { ...todo, completed: !todo.completed } : todo
)
};
case 'DELETE_TODO':
return {
...state,
todos: state.todos.filter(todo => todo.id !== action.payload.id)
};
default:
return state;
}
};
export default todoReducer;
今回はここまです〜。
Reduxは、コンポーネント間でもstateを共有できるようにするのに便利ということみたいです。
実際に、コードを書いてみてどのような感じになるのか気になります。
また、次回。