JavaScript、React、Reduxにおける「レデューサー」と呼ばれる理由を分かりやすく解説

2024-06-25

Reduxにおけるレデューサーが「レデューサー」と呼ばれる理由

関数としての役割

レデューサーは、状態アクションを引数として受け取り、新しい状態を返す純粋関数です。この動作は、JavaScriptの配列処理メソッドである「reduce」と似ています。

「reduce」メソッドは、配列の要素を順番に処理し、単一の値にまとめる役割を持ちます。一方、Reduxのレデューサーは、状態をアクションに基づいて更新し、新しい状態を生成する役割を担います。

この類似性から、状態を更新・生成するレデューサー関数は「レデューサー」と名付けられたのです。

状態を「圧縮」するイメージ

レデューサーは、アクションによって変化した状態を、より簡潔な表現へと「圧縮」するようなイメージで捉えることもできます。

Reduxにおける状態管理は、逐次的な状態変化を履歴として保持します。しかし、常にすべての履歴を保持するのは非効率的です。そこで、レデューサーは、現在の状態とアクション情報のみを用いて、必要な情報だけを圧縮した新しい状態を生成します。

この「圧縮」という観点からも、レデューサーという名称が適切であると言えます。

補足

レデューサーは、Reduxにおける状態更新の核となる重要な役割を担っています。その動作を理解することは、Reduxの全体像を把握する上で欠かせません。




    サンプルコード:Todoアプリにおけるレデューサー

    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 ? { ...todo, completed: !todo.completed } : todo
            )
          };
        default:
          return state;
      }
    };
    
    export default todoReducer;
    

    このコードでは、以下の処理が行われています。

    1. initialState には、初期状態として空の todos 配列を定義します。
    2. todoReducer 関数は、stateaction を引数として受け取り、新しい状態を返します。
    3. switch 文を使用して、アクションの種類ごとに処理を分岐します。
    4. ADD_TODO アクションの場合、新しいTodoアイテムを todos 配列に追加します。
    5. TOGGLE_TODO アクションの場合、指定されたIDのTodoアイテムの完了状態を反転します。
    6. デフォルトの処理として、現在の状態をそのまま返します。

    このサンプルコードはあくまで簡易的なものであり、実際のアプリケーションではより複雑な処理を行う必要が生じる可能性があります。しかし、レデューサーの基本的な動作を理解する上で参考となるでしょう。

    補足

    • 上記のコードでは、ES6のスプレッド構文 (...) とアロー関数を使用しています。これらの構文は、Reduxと併用されることが多いため、理解しておくことをお勧めします。
    • レデューサーは、複数のファイルを分割して記述することもできます。



    Redux レデューサーのその他の使用方法

    複数の状態プロパティを更新する

    レデューサーは、単一の狀態プロパティだけでなく、複数の狀態プロパティを同時に更新することができます。例として、カウンタアプリにおけるレデューサー実装を以下に示します。

    const initialState = {
      count: 0,
      message: 'クリックしてカウントアップ'
    };
    
    const counterReducer = (state = initialState, action) => {
      switch (action.type) {
        case 'INCREMENT':
          return {
            ...state,
            count: state.count + 1,
            message: 'カウントアップしました!'
          };
        case 'RESET':
          return {
            ...state,
            count: 0,
            message: 'カウントをリセットしました'
          };
        default:
          return state;
      }
    };
    
    export default counterReducer;
    

    このコードでは、INCREMENT アクションの場合には count プロパティと message プロパティを同時に更新し、RESET アクションの場合には count プロパティを 0 にリセットしています。

    ネストされた状態を更新する

    レデューサーは、ネストされた状態構造を更新することもできます。例として、ショッピングカートアプリにおけるレデューサー実装を以下に示します。

    const initialState = {
      cart: {
        items: [],
        totalPrice: 0
      }
    };
    
    const cartReducer = (state = initialState, action) => {
      switch (action.type) {
        case 'ADD_ITEM':
          const updatedItems = [...state.cart.items, action.payload];
          const totalPrice = updatedItems.reduce((sum, item) => sum + item.price, 0);
          return {
            ...state,
            cart: {
              items: updatedItems,
              totalPrice
            }
          };
        case 'REMOVE_ITEM':
          const filteredItems = state.cart.items.filter(item => item.id !== action.payload);
          const updatedTotalPrice = filteredItems.reduce((sum, item) => sum + item.price, 0);
          return {
            ...state,
            cart: {
              items: filteredItems,
              totalPrice: updatedTotalPrice
            }
          };
        default:
          return state;
      }
    };
    
    export default cartReducer;
    

    このコードでは、ADD_ITEM アクションの場合には cart.items 配列に新しいアイテムを追加し、cart.totalPrice プロパティを更新します。REMOVE_ITEM アクションの場合には cart.items 配列から指定されたアイテムを削除し、cart.totalPrice プロパティを更新します。

    非同期処理を行う

    レデューサーは、非同期処理を行うこともできます。ただし、非同期処理は直接的に行うのではなく、Reduxのミドルウェアを使用して行う必要があります。

    例として、APIからデータを取得する非同期処理を行うレデューサー実装を以下に示します。

    const initialState = {
      todos: []
    };
    
    const todoReducer = (state = initialState, action) => {
      switch (action.type) {
        case 'FETCH_TODOS':
          return {
            ...state,
            isLoading: true
          };
        case 'FETCH_TODOS_SUCCESS':
          return {
            todos: action.payload,
            isLoading: false
          };
        case 'FETCH_TODOS_FAILURE':
          return {
            ...state,
            error: action.payload,
            isLoading: false
          };
        default:
          return state;
      }
    };
    
    export default todoReducer;
    

    このコードでは、FETCH_TODOS アクションの場合には isLoading プロパティを true に設定し、非同期処理を開始します。非同期処理が成功した場合は FETCH_TODOS_SUCCESS アクションを、失敗した場合は FETCH_TODOS_FAILURE アクションをdispatchします。

    実際の非同期処理は、Reduxのミドルウェアを使用して実装する必要があります。

    Reduxレデューサーは、様々な方法で使用することができます。上記で紹介した例以外にも、様々なユースケースに対応できる柔軟な仕組みです。

    レデューサーを理解することは、Reduxを使ったアプリケーション開発において非常に重要です。ぜひ、サンプルコードなどを参考に、レデューサーを使いこなせるようになりましょう。

    • Redux チュートリアル - Redux [無効な URL を削除

    javascript reactjs redux


    JavaScriptにおけるcurrentTargetとtargetプロパティの決定的な違い:詳細解説とサンプルコード

    JavaScriptのイベントリスナーにおいて、event. target と event. currentTarget はどちらもイベント発生に関わる重要なプロパティですが、それぞれ異なる役割を果たします。混同しがちなポイントを押さえ、それぞれの特性を理解することが重要です。...


    Node.js開発で発生!process.env.NODE_ENVがundefinedになる謎を解き明かす

    process. env. NODE_ENVがundefinedになる理由はいくつかあります。設定されていないデフォルトでは、process. env. NODE_ENVは設定されていません。開発環境ではdevelopment、本番環境ではproductionなど、適切な値を設定する必要があります。...


    画像を切り替えたりフェードイン・フェードアウトしたり:JavaScriptでできる画像操作

    まず、<img> タグに id 属性を割り当て、JavaScript からその要素を簡単に操作できるようにします。次に、JavaScript コードで document. getElementById() メソッドを使用して <img> 要素を取得し、その src 属性を新しい画像の URL に変更します。...


    getDerivedStateFromProps メソッドを使ってprops変更時にstateを更新する方法

    useEffect Hookは、コンポーネントがマウントされたとき、アンマウントされたとき、またはpropsが変更されたときに実行される関数を登録するために使用されます。useEffect Hookを使って、props変更時にstateを更新するには、以下のようにします。...


    【まるっと解決】React Router v4.0.0 で「Uncaught TypeError: Cannot read property 'location' of undefined」が起きた時の対処法

    React Router v^4.0.0 で、以下のエラーが発生する場合があります。このエラーは、location オブジェクトにアクセスしようとしたときに発生します。location オブジェクトは、ブラウザの現在の URL 情報を含むオブジェクトです。...