🍑

RTK Queryの活用: フックを使う場合と関数を使う場合の実装例

2024/08/12に公開

Redux ToolkitのRTK Queryには、フックを使う場合と関数をディスパッチする場合の2つのアプローチが存在する。

フックを使う場合: 状態管理が簡単で、コードがシンプルになり、コンポーネントで直接利用できる。

ディスパッチする場合: 高い柔軟性を持ち、コンポーネント外でも使用可能であり、複雑なロジックに対応しやすい。

なお、Redux Toolkitでの「スライス」は主に状態管理を担当する。一方、RTK Queryでの「スライス」はAPI呼び出しやキャッシング、ステータス管理を行う。混乱を避けるため、この記事ではRTK Queryの「スライス」を「APIエンドポイント」と呼ぶことにする。

1. フックを使う場合: カウンターのAPI管理の実装例

counterApi.js
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';

// API定義
const counterApi = createApi({
  reducerPath: 'counterApi',
  baseQuery: fetchBaseQuery({ baseUrl: '/api' }), //ベースURLを指定 
  endpoints: (builder) => ({ //エンドポイントを定義
    //以下で、クエリとミューテーションを定義
    getCounter: builder.query({
      query: () => 'counter',
    }),
    incrementCounter: builder.mutation({
      query: (amount) => ({
        url: 'counter/increment',
        method: 'POST',
        body: { amount },
      }),
    }),
    decrementCounter: builder.mutation({
      query: (amount) => ({
        url: 'counter/decrement',
        method: 'POST',
        body: { amount },
      }),
    }),
  }),
});
//フックのエクスポート。これらのフックをコンポーネントで使用して、APIとのやり取りを行う
export const {
    useGetCounterQuery,
    useIncrementCounterMutation,
    useDecrementCounterMutation
} = counterApi;

export default counterApi;
store.js
// ストアへのAPIエンドポイントの追加
import { configureStore } from '@reduxjs/toolkit';
import { setupListeners } from '@reduxjs/toolkit/query';
import countersReducer from './features/counters/countersSlice';
import counterApi from './counterApi'; // APIファイルをインポート

const store = configureStore({ //ストアを作成
 // APIエンドポイントのリデューサーを追加
  reducer: { 
    counters: countersReducer,
    [counterApi.reducerPath]: counterApi.reducer,
  },
  // APIエンドポイントのミドルウェアを追加
  middleware: (getDefaultMiddleware) =>
    getDefaultMiddleware().concat(counterApi.middleware),
});
//setupListenersを呼び出して、リアルタイム更新やポーリングを有効にする
setupListeners(store.dispatch);

export default store;

フックを使用してAPIからデータを取得する方法

CounterComponent.js
import React from 'react';
import {
    useGetCounterQuery,
    useIncrementCounterMutation,
    useDecrementCounterMutation }
from './counterApi';

const CounterComponent = () => {
  //useGetCounterQueryフックを使用してカウンターの値を取得する
  const { data, error, isLoading } = useGetCounterQuery();
  // フックを使用してカウンターの値を変更する
  // フックは自動的にAPIリクエストを発行し、取得されたデータやリクエストのステータス
  //(ロード中、成功、失敗など)がReduxストア内で管理される。
  // このプロセスで、ストアの状態が変更されている
  const [incrementCounter] = useIncrementCounterMutation();
  const [decrementCounter] = useDecrementCounterMutation();

  if (isLoading) {
    return <div>Loading...</div>;
  }

  if (error) {
    return <div>Error: {error.message}</div>;
  }

  return (
    <div>
      <h1>Counter: {data?.value}</h1>
      <button onClick={() => incrementCounter(1)}>Increment</button>
      <button onClick={() => decrementCounter(1)}>Decrement</button>
    </div>
  );
};

export default CounterComponent;

2. 関数を使う場合: カウンターのAPI管理の実装例

counterApi.js
//... (上記のcounterApi.jsと同じ)

// フックの代わりに、APIエンドポイントを呼び出す関数を使用するためのエクスポート
export const { 
  getCounter, 
  incrementCounter, 
  decrementCounter 
} = counterApi.endpoints;

export default counterApi;
store.js
// 上記のstore.jsと同じ。

関数を使用してAPIからデータを取得する方法

CounterComponent.js
import React, { useEffect } from 'react';
import { useDispatch } from 'react-redux';
import { counterApi } from './counterApi';

const CounterComponent = () => {
  const dispatch = useDispatch();

  useEffect(() => {
    // useEffectを使って、コンポーネントがマウントされたときにデータを取得する
    const fetchCounter = async () => {
      const result = await
          // dispatchを使用してAPIからデータを取得する
      dispatch(counterApi.endpoints.getCounter.initiate());
    };

    fetchCounter();
  }, [dispatch]);

  const handleIncrement = async () => {
    const result = await
      // dispatchを使用してカウンターの値を変更する
      // APIリクエストを行うための関数 (initiate) がdispatchされ、その結果は
      // RTK Queryで処理され、データやステータスが自動的にReduxの状態に保存される  
      dispatch(counterApi.endpoints.incrementCounter.initiate(1));
  };

  const handleDecrement = async () => {
    const result = await
     // (同上)
      dispatch(counterApi.endpoints.decrementCounter.initiate(1));
    console.log(result.data);
  };

  return (
    <div>
      <h1>Counter Component</h1>
      <button onClick={handleIncrement}>Increment</button>
      <button onClick={handleDecrement}>Decrement</button>
    </div>
  );
};

export default CounterComponent;

Discussion