Reactで非同期データを取得する
React の useReducer
は、複雑な状態管理を扱うための強力なフックです。非同期データの取得と状態の更新を組み合わせることで、ダイナミックなユーザーインターフェースを実現できます。
基本的な手順
-
初期状態の定義
initialState
を定義します。これは、データのフェッチが完了する前の初期状態です。- 例えば、データがまだ取得されていないことを示す
loading
フラグや、エラーメッセージ用のフィールドを含めることができます。
-
Reducer 関数の定義
reducer
関数は、現在の状態とアクションを受け取り、新しい状態を返します。- アクションのタイプに基づいて、適切な状態の更新を行います。
- 非同期データの取得が完了したら、
FETCH_SUCCESS
などのアクションをディスパッチして、状態を更新します。
-
useReducer の使用
useReducer
を使って、initialState
とreducer
を初期化します。- 返される配列の最初の要素は現在の状態、2番目の要素はディスパッチ関数です。
-
非同期データの取得と状態の更新
useEffect
を使って、コンポーネントのマウント時に非同期データの取得を開始します。- データの取得が完了したら、
dispatch
関数を使ってFETCH_SUCCESS
アクションをディスパッチします。 - アクションペイロードには、取得したデータを含めます。
コード例
import React, { useReducer, useEffect } from 'react';
const initialState = {
loading: true,
error: null,
data: [],
};
const reducer = (state, action) => {
switch (action.type) {
case 'FETCH_SUCCESS':
return {
loading: false,
error: null,
data: action.payload,
};
case 'FETCH_ERROR':
return {
loading: false,
error: action.payload,
data: [],
};
default:
return stat e;
}
};
function MyComponent() {
const [state, dispatch] = useReducer(reducer, initialState);
useEffect(() => {
const fetchData = async () => {
try {
const response = await fetch('http s://api.example.com/data');
const data = await response.json ();
dispatch({ type: 'FETCH_SUCCESS', payload: data });
} catch (error) {
dispatch({ type: 'FETCH_ERROR', payload: error.message });
}
};
fetch Data();
}, []);
// ... コンポーネントのレンダリングロジック ...
}
注意点
- データの取得が完了する前に、ローディングインジケーターを表示するなど、ユーザーエクスペリエンスを考慮した設計をしましょう。
- エラーハンドリングを適切に行い、エラーメッセージを表示するなどの対応をしましょう。
- 非同期操作は
useEffect
内で行うことが一般的です。
import React, { useReducer, useEffect } from 'react';
// 初期状態
const initialState = {
loading: true, // データの読み込み中かどうか
error: null, // エラーメッセージ
data: [], // 取得したデータ
};
// Reducer 関数
const reducer = (state, action) => {
switch (action.type) {
case 'FETCH_SUCCESS':
return {
loading: false,
error: null,
data: action.payload, // 取得したデータ
};
case 'FETCH_ERROR':
return {
loading: false,
error: action.payload, // エラーメッセージ
data: [],
};
default:
return state;
}
};
function MyComponent() {
const [state, dispatch] = useReducer(reducer, initialState);
useEffect(() => {
const fetchData = async () => {
try {
const response = await fetch('http s://api.example.com/data');
const data = await response.json ();
dispatch({ type: 'FETCH_SUCCESS', payload: data });
} catch (error) {
dispatch({ type: 'FETCH_ERROR', payload: error.message });
}
};
fetch Data();
}, []);
// コンポーネントのレンダリング
return (
<div>
{state.loading ? (
<p>Loading...</p>
) : state.error ? (
<p>Error: {state.error}</p>
) : (
<ul>
{state.data.map(item => (
<li key={item.id}>{item.name}</li>
))}
</ul>
)}
</div>
);
}
コードの説明
-
loading
: データのフェッチが進行中かどうかを示すフラグ。初期値はtrue
。error
: エラーが発生した場合のメッセージ。初期値はnull
。data
: フェッチされたデータの配列。初期値は空の配列。
-
FETCH_SUCCESS
: データのフェッチが成功した場合、loading
をfalse
に、data
に取得したデータをセット。FETCH_ERROR
: エラーが発生した場合、loading
をfalse
に、error
にエラーメッセージをセット。
-
非同期データのフェッチ
useEffect
を使って、コンポーネントのマウント時にfetchData
関数を呼び出す。
-
コンポーネントのレンダリング
state.loading
がtrue
の場合、ローディングメッセージを表示。state.error
がnull
以外の場合、エラーメッセージを表示。- そうでない場合、取得したデータをリスト形式で表示。
ポイント
- 条件分岐を使って、状態に応じて異なる UI をレンダリングする。
dispatch
関数を使って、Reducer 関数を呼び出し、状態を更新する。fetch
API を使って、ネットワークリクエストを行い、データを取得する。useEffect
を使って、非同期操作をコンポーネントのマウント時に実行する。useReducer
を使って、複雑な状態管理を効率的に行うことができる。
useReducer
は、複雑な状態管理に適していますが、非同期データフェッチのみに特化した場合は、よりシンプルで直感的な手法があります。
useEffect と useState を組み合わせた手法
import React, { useState, useEffect } from 'react';
function MyComponent() {
const [data, setData] = useState([]);
const [isLoading, setIsLoading] = useState(tru e);
const [error, setError] = useState(null);
useEffect(() => {
const fetchData = async () => {
try {
const response = await fetch('https://api.example.com/ data');
const data = await response.json();
setData(data);
setIsLoading(false);
} catch (error) {
setError(error.message);
setIsLoading(false);
}
};
fetchData ();
}, []);
// ... コンポーネントのレンダリングロジック ...
}
この手法では、useState
を使ってデータ、ローディング状態、エラー状態を個別に管理します。useEffect
を使って、コンポーネントのマウント時に非同期データのフェッチを開始し、結果を setData
, setIsLoading
, setError
で更新します。
Custom Hook を使った手法
import { useState, useEffect } from 'react';
function useFetchData(url) {
const [data, setData] = useState([]);
const [isLoading, setIsLoading] = useStat e(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchData = async () => {
try {
const response = await fetch(url);
const data = await response.json();
setData( data);
setIsLoading(false);
} catch (error) {
setError(error.message);
setIsLoading(false);
}
};
fetchData();
}, [url]);
return { data, isLoading, error };
}
function MyComponent() {
const { data, isLoading, error } = useFetchData('https://api.example.com/data');
// ... コンポーネントのレンダリングロジック ...
}
この手法では、カスタムフック useFetchData
を作成して、非同期データのフェッチロジックをカプセル化します。コンポーネント内でこのフックを使用することで、データ、ローディング状態、エラー状態を簡単に取得できます。
どちらの手法を選ぶべきか?
- 再利用可能な非同期データフェッチロジック
カスタムフックが便利です。 - 複数の非同期操作や複雑な状態管理
useReducer
が適しています。 - シンプルな非同期データフェッチ
useEffect
とuseState
の組み合わせで十分です。
reactjs react-hooks