ミドルウェアは、ウェブ開発における新しいコンセプトではありません。これは、Express.jsなどのバックエンドフレームワークと一般的に関連付けられており、認証、ロギングなどが多く含まれます。その固有の利点から、ミドルウェアはフロントエンドで重要な位置を占めるようになっています。
Reactなどのフロントエンドフレームワークは、ステート管理中に複雑な機能を処理する方法としてそれを取り入れています。このガイドでは、Reactにおけるミドルウェアのコンセプトと、Reactアプリの機能を向上させ、アプリケーションフローを効果的に管理する方法を紹介します。
最後まで読み進めると、Reactミドルウェアの概念とRedux Thunkと組み合わせて実装する方法について理解が深まるでしょう。
Reactミドルウェアのシンプル化:アプリケーションロジックの簡素化
ミドルウェアとは何ですか?
その名前が示す通り、ミドルウェアとはアプリケーションの異なるコンポーネントの間に位置するレイヤーのことです。これは、これがなければ存在しない処理と機能を提供します。ミドルウェアは、1つのコンポーネントから別のコンポーネントに流れるリクエストを傍受し、特定のアクションを実行することができます。処理が完了すると、修正されたリクエストを次のミドルウェアまたはその意図した宛先に渡します。
これは主にバックエンドで使用されるコンセプトです。しかし、前述のように、Reactで同様の目的に役立つように適応されています。それでは、Reactにおけるミドルウェアの具体的な意味について詳しく見ていきましょう。
Reactにおけるミドルウェア
Reactでは、ミドルウェアは、関数のシリーズとして実装され、定義した順番に1つずつ実行されます。各関数はアプリの状態とアクションにアクセスできます。したがって、関数は状態を変更し新しいアクションをディスパッチすることができます。
Reactで状態が変更されると、アプリケーションはアクションをディスパッチし、それがReducerによって処理されます。Reducerは新しい状態を返し、それが保存されてアプリケーションに戻されます。
Reactのミドルウェアは、ディスパッチされたアクションとReducerに到達する前にそれを傍受します。その後、このプロセスにフックし、たとえばログを取るかAPI呼び出しを行うなど、このアクションに対していくつかのコードを実行することができます。
これが完了すると、修正されたアクションがReducerに渡されます(チェーン内に別のミドルウェアがない場合)。
Reactにおけるミドルウェアの例
Reactミドルウェアは主にReduxで見つけることができます。これは複雑な状態管理Reactライブラリです。Reduxには2つの主要なミドルウェアがあります:
- Thunk – このミドルウェアを使用してアクションを変更する方法について詳しく説明します。
- Saga。
それぞれがアクションへの異なる種類の変更を処理します。Reactでのミドルウェアの使用例を見る際に、middlewareの使用方法を見ていきます。他のReactライブラリ、例えばReact Queryもミドルウェアを使用します(後で詳しく説明します)。
これを実装する方法を見る前に、Reactでのミドルウェアの異なる使用方法について見ていきましょう。
Reactでのミドルウェアの使用方法
Reactミドルウェアは、次のようなユースケースで役立ちます:
デバッグとログ記録
Reactミドルウェアは、アプリケーション開発中に現在の状態やアクション、その他のデータなどをログに記録するために使用できます。さらに、これらは潜在的なバグやエラーを早期に特定するのに役立ちます。
Redux Logger(Redux用)を使用すると、Reduxアクションと状態の変更を簡単にデバッグできます。各ディスパッチされたアクションをインターセプトし、直前の状態、アクション、および次の状態をログに記録します。
認証と認可
状態を更新する前にユーザーを認証したい場合、Redux ThunkやSagaを使用して認証ワークフローを処理できます。アクションをインターセプトすると、このミドルウェアを使用してトークンを保存したり、アクションをディスパッチする前に認証をチェックしたり、トークンをリフレッシュしたりできます。
言い換えれば、これらのミドルウェアは、特定のルートにアクセスする際にユーザーが認証されているかどうかをチェックするのに役立ちます。
イベント駆動型の操作
Reactリデューサーは同期コードを実行するように設計されています。つまり、それに非同期のタスクを実行しようとすると機能しません。React ThunkなどのReactミドルウェアを使用すると、アクションをキャッチし、API呼び出しのような非同期タスクを実行し、その後にリデューサーに進むことができます。
データのキャッシング
React Query(別のReactミドルウェア)は、アプリケーションデータをキャッシュするのに効果的なツールとして機能することができます。APIの応答を自動的にキャッシュし、必要に応じて再検証します。その結果、キャッシュ内で必要な情報をチェックすることで、冗長なAPI呼び出しを回避するのに役立ちます。
パフォーマンスの向上
Reactミドルウェアは、不要なアクションを後で実行したり、イベントを遅延させたり、特定のタスクをバッチ処理したりすることで、アプリケーションのパフォーマンスを向上させるのにも役立ちます。APIの応答をキャッシュすることで、React QueryはReactアプリのパフォーマンスも向上させます。
Reactでミドルウェアを使用する方法
Reduxは、Reactアプリケーションでグローバルステートを管理する強力なツールです。ツールキットには複数のミドルウェアが含まれており、Reactアプリにカスタム機能を追加するために使用できます。最も一般的なものの1つはRedux Thunkです。
Redux Thunkの紹介
Thunkの実装に深く入る前に、それについていくつかの情報を集めて、効果的に使用できるようにしましょう。
Thunkとは?
一般的なプログラミングでは、「Thunk」は単に関数であり、関数の評価と実行を遅延させるために使用されます。特定の条件が満たされるまでアクションを延期する関数と考えることができます。
Reduxでは、ThunkはRedux Thunkミドルウェアと共に使用される特定の関数です。Redux Thunkは、Reactアプリ内で非同期操作を許可するように設計されています。以前に述べたように、Reducerは同期コードを実行するように構築されています。これは、アクションがディスパッチされると、Reducerが状態を直ちに更新することを意味します。
ミドルウェアとしてのRedux Thunk
Redux Thunkはミドルウェアとして機能します。これにより、プレーンなアクションオブジェクトの代わりに関数(スンク)を返すアクションクリエータを作成できます。これらの関数には非同期ロジックを含めることができます。スンクをディスパッチすると、Thunkミドルウェアがそれをインターセプトして関数を実行します。
thunk
内で非同期操作(例:APIコール)を実行し、操作が完了した時点でReduxストアを更新するための通常のアクションをディスパッチできます。
注: Redux Thunkは非同期および同期操作の両方を許可しますが、主な目的は非同期アクションの実行を容易にすることです。同期アクションのディスパッチを妨げるのではなく、非同期アクションを処理するメカニズムを提供します。
それでは、Redux Thunkを実装してReactミドルウェアを実際に見てみましょう。
Redux Thunkの実装のステップバイステップガイド
Reduxミドルウェアの実装には次の手順が含まれます:
ステップ1:ReduxをセットアップThunkとともに
Reactアプリケーションを作成し、依存関係をインストールします。
npx create-react-app newApp
ステップ2:Redux Thunkをインストール
次のコマンドを実行して、Redux Thunkをインストールします。
npm install redux-thunk
ステップ3:Thunkミドルウェアを有効にする
Enable the Thunk middleware in the Redux store.
import { configureStore } from '@reduxjs/toolkit';
import thunk from 'redux-thunk';
import rootReducer from './reducers'; // Combine all reducers here
const store = configureStore({
reducer: rootReducer,
middleware: [thunk],
});
export default store;
注意: Redux Toolkitを使用する場合、thunk
を明示的にインストールする必要はありません。
ステップ4:非同期関数の作成
スローンクは、状態を読み取るかアクションをディスパッチするためにdispatch
とgetstate
引数を受け取る別の関数を返します。以下はデータを取得するためのコード例です:
// アクションタイプ
const START_FETCH = 'START_FETCH';
const FETCH_SUCCESS = 'FETCH_SUCCESS';
const FETCH_ERROR = 'FETCH_ERROR';
// アクションクリエーター
const startFetch = () => ({ type: START_FETCH });
const fetchSuccess = (data) => ({ type: FETCH_SUCCESS, payload: data });
const fetchError = (error) => ({ type: FETCH_ERROR, payload: error });
// スローンクアクション
export const fetchData = () => {
return async (dispatch, getState) => {
dispatch(startFetch()); // Notify that the fetch has started
try {
const response = await fetch('https://jsonplaceholder.typicode.com/posts');
if (!response.ok) {
throw new Error('Failed to fetch data');
}
const data = await response.json();
dispatch(fetchSuccess(data)); // Send the fetched data to the store
} catch (error) {
dispatch(fetchError(error.message)); // Handle any errors
}
};
};
ステップ5:リデューサーでディスパッチされたアクションの処理
リデューサーを更新することでディスパッチされたアクションを処理します。
const initialState = {
data: [],
isLoading: false,
error: null,
};
export const dataReducer = (state = initialState, action) => {
switch (action.type) {
case START_FETCH:
return { ...state, isLoading: true, error: null };
case FETCH_SUCCESS:
return { ...state, isLoading: false, data: action.payload };
case FETCH_ERROR:
return { ...state, isLoading: false, error: action.payload };
default:
return state;
}
};
ステップ6:コンポーネントからスローンクをディスパッチする
useDispatch
フックを使用してReactプロジェクトでスローンクをディスパッチします。
import React, { useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { fetchData } from './actions';
const DataComponent = () => {
const dispatch = useDispatch();
const { data, isLoading, error } = useSelector((state) => state.data);
useEffect(() => {
dispatch(fetchData()); // Trigger the thunk to fetch data
}, [dispatch]);
if (isLoading) return <p>Loading...</p>;
if (error) return <p>Error: {error}</p>;
return (
<ul>
{data.map((item) => (
<li key={item.id}>{item.title}</li>
))}
</ul>
);
};
export default DataComponent;
createAsyncThunkの使用
Redux Toolkitには、非同期関数の高レベルロジックを定義し、それらをディスパッチし、エラーを迅速に処理するための組み込みAPIが含まれています。非同期関数の特定のユースケースの抽象化を提供するため、createAsyncThunk
はスローンクのすべてのユースケースには適用されないことに注意してください。
createAsyncThunk
の例の実装は以下の通りです:
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
export const dataFetch = createAsyncThunk('data/fetchData', async () => {
const response = await fetch('https://api.example.com/data');
return response.json();
});
const dataSlice = createSlice({
name: 'data',
initial_State: { data: [], loading: false, error: null },
extraReducers: (builder) => {
builder
.addCase(dataFetch.pending, (state) => {
state.loading = true;
state.error = null;
})
.addCase(dataFetch.fulfilled, (state, action) => {
state.loading = false;
state.data = action.payload;
})
.addCase(dataFetch.rejected, (state, action) => {
state.loading = false;
state.error = action.error.message;
});
},
});
export default dataSlice.reducer;
今、dataFetch
スローンクをディスパッチするために、以下のコードを使用してください:
dispatch(dataFetch ());
React Middleware – Redux Thunkの使用方法
スローンクは、以下の目的に使用できますが、これに限定されません:
- コンポーネントから複雑なロジックを抽象化する。
- 非同期リクエストとロジックの実行。
- シリーズで複数のアクションをディスパッチする関数を記述します。
他の状態管理ライブラリにおけるReactミドルウェア
ReduxはReactライブラリの中でミドルウェアを活用する唯一のものではありません。他の状態管理ライブラリでもミドルウェアを見つけることができますが、これらのライブラリでのこの概念の実装はReduxとは異なります。
MobX
MobXにはReduxのような伝統的なミドルウェアはありません。代わりに、インターセプター、オブザーバー、リアクションなどのメカニズムを介して類似の機能を提供しています。これらのメカニズムにより、MobXの状態の変更を観察し、それに反応することができます。このように、MobXはReduxで通常ミドルウェアが処理する副作用、ログ記録、およびその他のタスクを処理する方法を提供しています。
Recoil
RecoilはReduxと同じようにミドルウェアをサポートしていません。なぜなら必要がないからです。特定の関数を使用して状態の断片(アトム)を直接更新することができます。ミドルウェアでインターセプトできるようなリデューサーへのアクションのディスパッチはありません。非同期操作を処理するために、Selectors(アトムや他のSelectorsに依存する派生状態)が使用されます。
Zustand
これはシンプルなAPIで状態を管理し、Reduxと同様にログ記録や状態の保存のためのネイティブミドルウェアをサポートしています。
XState
XStateはonTransition
、actions
、 およびservices
などのフックを使用して、ミドルウェアのような動作をサポートしています。これらのフックは状態の遷移をインターセプトして変更するのに役立ちます。
結論
ミドルウェアは、ウェブアプリケーションの異なるコンポーネントをつなぎ合わせ、データフローと処理を改善するための橋渡しとして機能します。これらはバックエンドで広く使用されてきましたが、最近ではフロントエンドでも使用例が見つかっています。
Reactでは、ミドルウェアを実装するさまざまな方法があります。これらは通常、ReduxやMobXなどの状態管理ライブラリにリンクされています。最も一般的に使用されるReactミドルウェア、Thunkは、Reduxライブラリに含まれています。Thunkは遅延タスクを実行するコードブロックです。
この記事では、Reactのミドルウェア、Reduxライブラリ、およびRedux Thunkについて探ってきました。また、データの取得とアクションのディスパッチのためにRedux Thunkミドルウェアを実装する手順もカバーしました。
Source:
https://dzone.com/articles/demystifying-react-middleware-bridging-apis-and-co