Redux学習 その1 : Reduxとは、基礎知識[Store(ストア)、Action(アクション)、Dispatch(ディスパッチ)、Reducer(リデューサー)]

Reactでは、stateを使用することで状態管理を行なっていました。

ただ、stateはそのコンポーネントでしかアクセスできませんでした。

つまり、他のコンポーネントからアクセスができず、同じstateを使用したくても難しいようでした。

これを解決するのがReduxのようです。

コンポーネント : UIの一部を構成する部品であり、再利用性と保守性を高めるために作成されます。Reactコンポーネントは、JavaScriptのクラスまたは関数として定義されることが一般的です。

Reduxの目的

  1. 状態の一貫性: アプリケーションの状態は、アプリケーション内のさまざまなコンポーネントによって変更されることがあります。これにより、状態の一貫性を保つことが難しくなる場合があります。Reduxでは、状態を一元的に管理し、直接変更することを避けることで、状態の一貫性を確保します。
  2. 可予測な状態変更: Reduxは、状態の変更を「アクション」と呼ばれるオブジェクトとして表現します。これにより、状態変更を事前に予測可能な方法で実行できます。アクションは、アプリケーション内のどこからでもディスパッチ(送信)されることができます。
  3. アプリケーションのデバッグとテスト: 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() というメソッドがあり、Redux store を作成するために使用します。 このメソッドは、必須の引数として reducer 関数を受け取ります。 reducer 関数についてはこの後のチャレンジで説明します。コードエディターではすでに定義が済んでいて、 単に state を引数に取り、state を返します。

store 変数を宣言し、それを createStore() メソッドに割り当て、引数として reducer を渡してください。

https://www.freecodecamp.org/japanese/learn/front-end-development-libraries/redux/create-a-redux-store

下記のような解答になりました。

となりました。

上記のコードの中で、

const reducer = (state = 5) => {...}

の部分ですが、デフォルト引数というものでした。

デフォルト引数 : 関数が呼び出される際に引数が指定されなかった場合、デフォルト値が使用される。

これで、Storeが作成されました。

Redux ストアから state を取得する

Reduxストからstateを取得するには、

getState() メソッドを使用するようです。

下記のような課題がありました。

コードエディターで、前のチャレンジのコードが、より簡潔な内容に書き換えられています。 store.getState() を使用して、store から state を取得し、それを新しい変数 currentState に割り当ててください。

https://www.freecodecamp.org/japanese/learn/front-end-development-libraries/redux/get-state-from-the-redux-store

下記のような解答になりました。

Redux のアクションを定義する

Reduxの状態を変更するを記述するオブジェクトを作成します。

Reduxでのアクションを定義する方法は、type プロパティを持つオブジェクトを宣言するだけのようです。

const action = {type : 'LOGIN'}

となります。

アクションクリエイターを定義する

アクションを作成後、次のステップとして、Reduxストアにアクションを送信して、状態を更新できるようにする必要があるようです。

この処理を実行するのに使われるのが、アクションクリエイターだそうです。

アクションクリエイターは、単純にアクションを返すJavaScript関数だそうです。

別の言い方をすれば、アクションクリエイターは、アクションイベントを表すオブジェクトを作成するそうです。

下記のような課題がありました。

呼び出されたときに action オブジェクトを返す actionCreator() という関数を定義してください。

https://www.freecodecamp.org/japanese/learn/front-end-development-libraries/redux/define-an-action-creator

解答としては、下記のようになりました。

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つのアクションを定義しました:

  1. ADD_TODO: 新しいToDoを追加するアクション。payloadプロパティには新しいToDoの内容が含まれます。
  2. TOGGLE_TODO: ToDoの完了状態を切り替えるアクション。payloadプロパティには対象のToDoのIDが含まれます。
  3. DELETE_TODO: ToDoを削除するアクション。payloadプロパティには対象のToDoのIDが含まれます。

アクションイベントをディスパッチする

アクションをReduxストアに送信することをディスパッチと呼ぶそうです。

どういう時に、ディスパッチを使用するのか調べてみたところ

具体的には、Reduxのアプリケーションで何かしらのイベントが発生した場合に、それに対応するアクションをディスパッチ(送信)することで、ストア内の状態を変更したり、特定の処理を実行したりするようです。

store.dispatch(action);

ここで、storeはReduxのストアオブジェクトであり、actionはディスパッチするアクションオブジェクトです。

下記のような課題がありました。

コードエディターの Redux ストアに初期化済みの state があります。これは login プロパティを含んでいるオブジェクトで、プロパティは現在 false に設定されています。 また、loginAction() というアクションクリエイターがあり、これはタイプ LOGIN のアクションを返します。 dispatch メソッドを呼び出して LOGIN アクションを Redux ストアにディスパッチし、loginAction() によって作成されたアクションを渡してください。

https://www.freecodecamp.org/japanese/learn/front-end-development-libraries/redux/dispatch-an-action-event

解答は下記のようになりました。

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 を直接変更することは決して許されません。

下記のような課題がありました。

コードエディターに、以前に紹介した例と、reducer 関数の冒頭部分があります。 reducer 関数の本体を入力して、タイプ 'LOGIN' のアクションを受け取った場合に login が true に設定された state オブジェクトを返すようにしてください。 それ以外の場合は、現在の state を返してください。 現在の state とディスパッチされた action がレデューサーに渡されるため、action.type を使用してアクションタイプに直接アクセスすることができます。

https://www.freecodecamp.org/japanese/learn/front-end-development-libraries/redux/handle-an-action-in-the-store

解答としては下記のようになりました。

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を共有できるようにするのに便利ということみたいです。

実際に、コードを書いてみてどのような感じになるのか気になります。

また、次回。