Reactは、コンポーネントベースの開発手法で広く使われているJavaScriptライブラリです。その中で、非同期データを親コンポーネントから子コンポーネントに渡すシチュエーションは、多くの開発者が直面する課題の一つです。たとえば、外部APIからデータを取得し、それを子コンポーネントで表示する場合、ローディング状態やエラーハンドリングなど、さまざまな課題が発生します。本記事では、Reactにおける非同期データの基本的な扱い方から、親子間でのデータ伝達、ローディングの実装、エラーハンドリングの実践的な手法までを徹底的に解説します。このガイドを通じて、Reactアプリケーションでより堅牢でスムーズな非同期処理を実現できるようになるでしょう。
Reactにおける非同期データの基本
Reactで非同期データを扱う際には、主に以下のポイントを押さえる必要があります。
非同期処理とは
非同期処理とは、時間がかかる処理(例:APIリクエスト、ファイル読み込みなど)が完了する前に、他の操作を続行するプログラミング手法です。Reactでは、この非同期データの取得や管理が重要な役割を果たします。
Reactのライフサイクルと非同期処理
Reactコンポーネントのライフサイクル(マウント、更新、アンマウント)は、非同期処理と密接に関係しています。具体的には、データの取得は通常以下のようなタイミングで行われます:
- マウント時(useEffectフックを利用): コンポーネントが初めて表示されるときにデータを取得します。
- 更新時(依存配列を指定): 依存する状態やプロパティが変化したときにデータを再取得します。
非同期データとステート管理
非同期データは通常、以下のステートで管理されます:
- データそのもの: APIから取得したデータを保存するためのステート。
- ローディング状態: データ取得中かどうかを示すステート。
- エラー情報: 取得に失敗した際のエラーを格納するステート。
Reactでは、これらをuseState
やuseReducer
などを用いて管理することが一般的です。
import React, { useState, useEffect } from 'react';
function ExampleComponent() {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
fetch('https://api.example.com/data')
.then((response) => response.json())
.then((result) => {
setData(result);
setLoading(false);
})
.catch((err) => {
setError(err);
setLoading(false);
});
}, []);
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
return <div>{JSON.stringify(data)}</div>;
}
このように、非同期データの処理はReactにおいて非常に重要な役割を果たします。次章では、親コンポーネントでのデータ取得の具体例について詳しく解説します。
非同期データを親コンポーネントで取得する方法
Reactアプリケーションでは、非同期データを親コンポーネントで取得してから子コンポーネントに渡すのが一般的です。これにより、データ取得のロジックを一箇所に集約でき、コードの可読性と再利用性が向上します。
親コンポーネントでのデータ取得の基本的な流れ
親コンポーネントでデータを取得する際には、以下の手順を踏みます:
- ステートを定義: データ、ローディング状態、エラー情報のステートを用意します。
- 非同期関数を作成: データ取得処理を行う関数を記述します。
useEffect
フックで呼び出し: コンポーネントのマウント時や更新時にデータを取得します。
実装例
以下は、非同期データを親コンポーネントで取得する基本的な実装例です。
import React, { useState, useEffect } from 'react';
import ChildComponent from './ChildComponent';
function ParentComponent() {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchData = async () => {
setLoading(true);
try {
const response = await fetch('https://api.example.com/data');
if (!response.ok) throw new Error('Network response was not ok');
const result = await response.json();
setData(result);
} catch (err) {
setError(err);
} finally {
setLoading(false);
}
};
fetchData();
}, []); // 空の依存配列でマウント時にのみ実行
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
return <ChildComponent data={data} />;
}
コードのポイント
- 非同期関数の使用:
fetchData
は非同期関数として定義されており、API呼び出しを含むロジックを管理します。 - エラーのキャッチ:
try-catch
を使用してエラーを捕捉し、ユーザーに適切なメッセージを表示します。 - ローディング状態の管理: データ取得中の状態を
loading
ステートで管理し、ユーザー体験を向上させます。
親コンポーネントの役割の重要性
- データの取得ロジックを集中管理することで、アプリケーションの構造がシンプルになります。
- 子コンポーネントはデータの表示やUIに専念できるため、分離された責任のある設計が可能です。
次章では、取得した非同期データをどのように子コンポーネントに渡すかを解説します。
子コンポーネントへのデータ渡しの方法
Reactでは、親コンポーネントで取得したデータを子コンポーネントに渡す際にprops
を利用します。この方法により、親コンポーネントでの状態管理を保ちながら、子コンポーネントでデータを柔軟に使用することができます。
プロパティ(props)を用いたデータの伝達
props
は、Reactコンポーネント間でデータを受け渡すための手段です。親コンポーネントから子コンポーネントを呼び出す際に渡された値は、子コンポーネント内で読み取ることができます。
基本的な使用例
以下のコードは、親コンポーネントから取得した非同期データをprops
を介して子コンポーネントに渡す例です。
import React, { useState, useEffect } from 'react';
import ChildComponent from './ChildComponent';
function ParentComponent() {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchData = async () => {
try {
const response = await fetch('https://api.example.com/data');
const result = await response.json();
setData(result);
} catch (err) {
setError(err);
} finally {
setLoading(false);
}
};
fetchData();
}, []);
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
return <ChildComponent data={data} />;
}
export default ParentComponent;
import React from 'react';
function ChildComponent({ data }) {
return (
<div>
<h2>Data from Parent:</h2>
<pre>{JSON.stringify(data, null, 2)}</pre>
</div>
);
}
export default ChildComponent;
コードのポイント
- 親コンポーネントでのデータ取得:
ParentComponent
で非同期データを取得し、その結果をdata
ステートに保存します。 props
でデータを渡す: 子コンポーネントChildComponent
に、<ChildComponent data={data} />
の形でdata
を渡しています。- 子コンポーネントでの受け取り: 子コンポーネントでは、
props
として受け取ったdata
を表示しています。
ローディング状態やエラー情報の共有
props
を使用してデータだけでなく、ローディング状態やエラー情報も子コンポーネントに渡すことができます。
return <ChildComponent data={data} loading={loading} error={error} />;
これにより、子コンポーネント側でデータが未取得の状態やエラー状態に応じたUIを実装できるようになります。
利点と考慮点
- 利点:
props
によるデータ渡しはシンプルで明示的なデータフローを保てます。 - 考慮点: 親コンポーネントが複雑になると、渡す
props
の数が増加し、保守性が低下する場合があります。この問題に対処するには、後述の状態管理ツール(例: Redux, Context API)の活用を検討してください。
次章では、非同期データのロード中に適切なUIを表示する方法について解説します。
非同期データのロード中のUI表示
非同期データを取得する際、データが準備できるまでの間に適切なローディングUIを表示することは、ユーザー体験を向上させる重要なポイントです。Reactでは、ローディング状態を管理することで、データ取得中の状態を簡単にユーザーに伝えることができます。
ローディングUIの基本実装
非同期データのロード中にUIを表示するには、データ取得の状態を管理するloading
ステートを利用します。以下はその基本的な実装例です。
import React, { useState, useEffect } from 'react';
function LoadingExample() {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
setLoading(true);
fetch('https://api.example.com/data')
.then((response) => response.json())
.then((result) => setData(result))
.finally(() => setLoading(false));
}, []);
if (loading) {
return <p>Loading...</p>;
}
return <div>Data Loaded: {JSON.stringify(data)}</div>;
}
export default LoadingExample;
ローディングインジケーターのデザイン
単純なテキストだけでなく、視覚的にわかりやすいローディングインジケーターを追加することで、ユーザー体験を向上させられます。以下は、CSSを使用したシンプルなスピナーの例です。
function LoadingSpinner() {
return (
<div className="spinner">
<div className="double-bounce1"></div>
<div className="double-bounce2"></div>
</div>
);
}
export default LoadingSpinner;
.spinner {
width: 40px;
height: 40px;
position: relative;
margin: auto;
}
.double-bounce1,
.double-bounce2 {
width: 100%;
height: 100%;
border-radius: 50%;
background-color: #3498db;
opacity: 0.6;
position: absolute;
animation: bounce 2s infinite ease-in-out;
}
.double-bounce2 {
animation-delay: -1s;
}
@keyframes bounce {
0%, 100% {
transform: scale(0);
}
50% {
transform: scale(1);
}
}
このスピナーをロード中のUIとして表示するには、次のように実装します。
if (loading) {
return <LoadingSpinner />;
}
ローディングUIのベストプラクティス
- 簡潔でわかりやすいデザイン: 複雑すぎるUIはユーザーを混乱させる可能性があります。シンプルで直感的なデザインを心がけましょう。
- 期待感を与えるメッセージ: 「データを読み込んでいます」など、進行中の作業を説明するメッセージを追加することで、ユーザーに安心感を与えます。
- データ読み込み時間の最小化: サーバーサイドでのパフォーマンス改善やキャッシュの利用など、データ取得時間を短縮する工夫も重要です。
親から子コンポーネントへのローディング状態の伝達
非同期データ取得が親コンポーネントで行われる場合、loading
ステートを子コンポーネントにprops
として渡して、子コンポーネントでローディングUIを表示することができます。
<ChildComponent data={data} loading={loading} />;
次章では、非同期データ取得におけるエラーハンドリングについて詳しく解説します。
非同期データ取得のエラーハンドリング
非同期データ取得では、エラーが発生する可能性を常に考慮する必要があります。ネットワーク障害やAPIの応答エラーなど、さまざまな要因がデータ取得に影響を与えるため、適切なエラーハンドリングを実装することは、アプリケーションの信頼性を向上させる重要な要素です。
エラーの検出と管理
非同期データ取得時のエラーを管理するには、try-catch
ブロックやPromise
の.catch()
を活用します。また、エラー情報を格納するためのステートを用意します。
ステートでエラーを管理する例
以下のコードは、非同期データ取得時にエラーを検出し、それをステートに保存する例です。
import React, { useState, useEffect } from 'react';
function ErrorHandlingExample() {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchData = async () => {
setLoading(true);
setError(null);
try {
const response = await fetch('https://api.example.com/data');
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const result = await response.json();
setData(result);
} catch (err) {
setError(err);
} finally {
setLoading(false);
}
};
fetchData();
}, []);
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
return <div>Data Loaded: {JSON.stringify(data)}</div>;
}
export default ErrorHandlingExample;
ユーザーへのエラー表示
エラーが発生した場合は、適切なエラーメッセージをユーザーに表示することが重要です。メッセージは具体的かつ簡潔にすることで、問題の内容を正確に伝えられます。
カスタムエラーメッセージの例
エラーの種類に応じて、カスタムメッセージを表示することもできます。
if (error) {
if (error.message.includes('Network')) {
return <p>ネットワークエラーが発生しました。接続を確認してください。</p>;
}
return <p>エラーが発生しました: {error.message}</p>;
}
親から子コンポーネントへのエラー情報の伝達
エラー情報を子コンポーネントに伝える場合は、props
を使用します。これにより、エラーが発生した際に子コンポーネントで適切な対応を取ることができます。
<ChildComponent data={data} loading={loading} error={error} />;
子コンポーネントでは、渡されたエラー情報を基に適切なUIを表示します。
function ChildComponent({ data, loading, error }) {
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
return <div>Data: {JSON.stringify(data)}</div>;
}
エラーハンドリングのベストプラクティス
- 詳細なエラーメッセージ: エラーの原因を明確にし、ユーザーが次に取るべきアクションを示唆します。
- 再試行機能の提供: エラーが発生した場合に再試行できるボタンを用意すると、ユーザー体験が向上します。
- ロギング: エラー情報をサーバーに記録し、問題の早期発見と修正につなげます。
if (error) {
return (
<div>
<p>Error: {error.message}</p>
<button onClick={() => window.location.reload()}>再試行</button>
</div>
);
}
次章では、子コンポーネント側でのエラーハンドリングの方法について詳しく説明します。
子コンポーネント側でのエラーハンドリング
非同期データ取得におけるエラーハンドリングは、親コンポーネントだけでなく、子コンポーネント側でも重要です。子コンポーネントでは、親から渡されたエラー情報に基づいて適切なUIを表示したり、独自のロジックで追加のエラーハンドリングを実装したりします。
親コンポーネントからエラー情報を受け取る
親コンポーネントが非同期データ取得を担当する場合、エラー情報をprops
として子コンポーネントに渡します。子コンポーネントは受け取ったエラー情報を利用してエラーメッセージや代替コンテンツを表示します。
function ChildComponent({ data, loading, error }) {
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
return <div>Data: {JSON.stringify(data)}</div>;
}
子コンポーネントでの独自エラーハンドリング
子コンポーネント側で非同期処理を行う場合や、特定の条件下で追加のエラー処理が必要な場合、独自のエラーハンドリングを実装することができます。
非同期処理を伴う子コンポーネントの例
以下は、子コンポーネントが非同期でデータを処理しつつエラーを管理する例です。
import React, { useState, useEffect } from 'react';
function ChildComponentWithAsync() {
const [localData, setLocalData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchData = async () => {
try {
const response = await fetch('https://api.example.com/child-data');
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const result = await response.json();
setLocalData(result);
} catch (err) {
setError(err);
} finally {
setLoading(false);
}
};
fetchData();
}, []);
if (loading) return <p>Loading in Child...</p>;
if (error) return <p>Child Error: {error.message}</p>;
return <div>Child Data: {JSON.stringify(localData)}</div>;
}
export default ChildComponentWithAsync;
エラーバウンダリを用いたエラーキャッチ
子コンポーネントでエラーが発生する場合に備え、エラーバウンダリを活用してReactツリー内のエラーをキャッチすることができます。エラーバウンダリはReactのcomponentDidCatch
ライフサイクルメソッドまたはErrorBoundary
コンポーネントで実装します。
エラーバウンダリの例
import React, { Component } from 'react';
class ErrorBoundary extends Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError() {
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
console.error('Error captured in boundary:', error, errorInfo);
}
render() {
if (this.state.hasError) {
return <p>Something went wrong. Please try again later.</p>;
}
return this.props.children;
}
}
export default ErrorBoundary;
エラーバウンダリを子コンポーネントに適用するには、次のようにラップします。
<ErrorBoundary>
<ChildComponent />
</ErrorBoundary>
エラーハンドリングのベストプラクティス
- 親と子で役割を分担: 親でデータ取得のエラーを管理し、子ではそのエラーを受け取りUIに反映します。
- ローカルなエラー管理: 子コンポーネント内の非同期処理には独自のエラー処理を実装します。
- エラーバウンダリの活用: 子コンポーネントが予期しないエラーを起こす可能性がある場合は、エラーバウンダリを使用してアプリ全体のクラッシュを防ぎます。
次章では、状態管理ツールを活用した非同期データの効率的な管理方法について説明します。
状態管理ツールの活用例
Reactアプリケーションが大規模になると、複数のコンポーネント間で非同期データを管理する必要が出てきます。この場合、状態管理ツールを使用することで、データの一元管理やロジックの分散を防ぎ、アプリケーションを効率的に構築できます。本章では、代表的な状態管理ツールであるReduxやContext APIを活用した非同期データ管理の例を解説します。
Reduxを利用した非同期データ管理
Reduxは、アプリケーション全体の状態を単一のストアで管理するツールです。非同期データ取得には、redux-thunk
やredux-saga
といったミドルウェアを利用します。
Reduxの基本構成
非同期データを管理する際のReduxの基本的な構成は以下の通りです。
- アクション: データ取得のリクエスト、成功、失敗を表すアクションを定義します。
- リデューサー: アクションに応じて状態を更新します。
- 非同期処理: ミドルウェア(例:
redux-thunk
)を用いてAPIリクエストを処理します。
実装例
// actions.js
export const fetchDataRequest = () => ({ type: 'FETCH_DATA_REQUEST' });
export const fetchDataSuccess = (data) => ({ type: 'FETCH_DATA_SUCCESS', payload: data });
export const fetchDataFailure = (error) => ({ type: 'FETCH_DATA_FAILURE', payload: error });
// reducer.js
const initialState = {
data: null,
loading: false,
error: null,
};
export default function dataReducer(state = initialState, action) {
switch (action.type) {
case 'FETCH_DATA_REQUEST':
return { ...state, loading: true, error: null };
case 'FETCH_DATA_SUCCESS':
return { ...state, loading: false, data: action.payload };
case 'FETCH_DATA_FAILURE':
return { ...state, loading: false, error: action.payload };
default:
return state;
}
}
// thunks.js
export const fetchData = () => async (dispatch) => {
dispatch(fetchDataRequest());
try {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
dispatch(fetchDataSuccess(data));
} catch (error) {
dispatch(fetchDataFailure(error));
}
};
コンポーネントでの使用
Reduxストアからデータを取得し、非同期処理をトリガーします。
import React, { useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { fetchData } from './thunks';
function ReduxExample() {
const { data, loading, error } = useSelector((state) => state.data);
const dispatch = useDispatch();
useEffect(() => {
dispatch(fetchData());
}, [dispatch]);
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
return <div>Data: {JSON.stringify(data)}</div>;
}
export default ReduxExample;
Context APIを利用した非同期データ管理
Reduxを導入するほどではない中小規模のアプリケーションでは、Context APIが便利です。Context APIはReactに組み込まれており、グローバルなデータ管理を簡易に行えます。
実装例
import React, { createContext, useState, useContext, useEffect } from 'react';
const DataContext = createContext();
export const DataProvider = ({ children }) => {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchData = async () => {
setLoading(true);
try {
const response = await fetch('https://api.example.com/data');
const result = await response.json();
setData(result);
} catch (err) {
setError(err);
} finally {
setLoading(false);
}
};
fetchData();
}, []);
return (
<DataContext.Provider value={{ data, loading, error }}>
{children}
</DataContext.Provider>
);
};
export const useData = () => useContext(DataContext);
コンポーネントでの使用
useContext
フックを用いてデータを取得します。
import React from 'react';
import { useData } from './DataProvider';
function ContextExample() {
const { data, loading, error } = useData();
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
return <div>Data: {JSON.stringify(data)}</div>;
}
export default ContextExample;
状態管理ツール活用のベストプラクティス
- アプリの規模に応じた選択: 小規模ではContext API、中~大規模ではReduxを採用する。
- 非同期処理の分離: データ取得ロジックをアクションやカスタムフックに分離してコンポーネントをシンプルに保つ。
- エラーハンドリングの統一: グローバルなエラーハンドリングを導入し、ユーザーに一貫したメッセージを提供する。
次章では、React QueryやSWRなどのライブラリを活用した高度な非同期データ管理の方法を解説します。
高度な非同期データ管理の応用例
React QueryやSWRといったデータフェッチングライブラリを使用すると、非同期データの取得、キャッシング、リトライなどの機能を簡単に実装できます。これらのライブラリは、効率的で信頼性の高いデータ管理を提供し、特にリアルタイムデータやキャッシュの活用が重要なアプリケーションで役立ちます。
React Queryを用いたデータ管理
React Queryは、サーバー状態を効率的に管理するためのライブラリです。非同期データの取得、キャッシュ、リトライ、自動再フェッチなどの機能を備えています。
基本的な使用例
以下のコードでは、React Queryを使用してAPIデータを取得し、キャッシュする方法を示します。
import React from 'react';
import { useQuery, QueryClient, QueryClientProvider } from 'react-query';
const queryClient = new QueryClient();
function fetchData() {
return fetch('https://api.example.com/data').then((response) => response.json());
}
function ReactQueryExample() {
const { data, error, isLoading } = useQuery('exampleData', fetchData);
if (isLoading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
return <div>Data: {JSON.stringify(data)}</div>;
}
export default function App() {
return (
<QueryClientProvider client={queryClient}>
<ReactQueryExample />
</QueryClientProvider>
);
}
主な特徴
- キャッシュ機能: データ取得結果をキャッシュし、同じデータへの再リクエストを最小化します。
- 自動再フェッチ: 背景で自動的にデータを更新し、最新状態を維持します。
- エラーハンドリング: リトライ機能により、一時的な障害があっても再試行を試みます。
SWRを用いたデータ管理
SWR(Stale-While-Revalidate)は、Next.jsの開発元であるVercelが提供するデータフェッチングライブラリで、最新のデータを取得しつつキャッシュのデータを活用する仕組みを備えています。
基本的な使用例
以下は、SWRを使用して非同期データを管理する例です。
import useSWR from 'swr';
const fetcher = (url) => fetch(url).then((res) => res.json());
function SWRExample() {
const { data, error, isLoading } = useSWR('https://api.example.com/data', fetcher);
if (isLoading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
return <div>Data: {JSON.stringify(data)}</div>;
}
export default SWRExample;
主な特徴
- Stale-While-Revalidate: 既存のキャッシュを即座に表示しながら、新しいデータをバックグラウンドで取得します。
- 最小限の設定: シンプルなAPIで手軽に利用できます。
- データ共有: 同一のキーを持つ複数のコンポーネント間でデータを共有します。
React QueryとSWRの比較
特徴 | React Query | SWR |
---|---|---|
用途 | サーバー状態の完全管理 | キャッシュを使ったデータ取得 |
機能 | 自動再フェッチ、リトライ、キャッシュ管理 | キャッシュ、Stale-While-Revalidate |
設定の容易さ | 中程度 | 高い |
エコシステム | 広範囲(React専用) | 軽量(React以外でも利用可能) |
応用例: リアルタイムデータの管理
リアルタイムデータが必要なアプリケーションでは、React QueryやSWRの自動更新機能が非常に役立ちます。以下は、ポーリング(一定間隔でのデータ再取得)の例です。
const { data, error, isLoading } = useQuery('realtimeData', fetchData, {
refetchInterval: 5000, // 5秒ごとに再フェッチ
});
高度なエラーハンドリング
React QueryやSWRでは、エラーをキャッチし、グローバルエラーハンドラーを設定することが可能です。
const queryClient = new QueryClient({
defaultOptions: {
queries: {
onError: (error) => {
console.error('Global error:', error);
},
},
},
});
活用のベストプラクティス
- キャッシュキーの設計: キャッシュキーは一意で明確にすることで、データの競合を防ぎます。
- リトライの設定: ネットワークエラー時に自動リトライを有効化し、一時的なエラーを処理します。
- リアルタイム更新の最適化: 必要な頻度で自動再フェッチを設定し、アプリのパフォーマンスを向上させます。
次章では、サンプルコードを用いた演習問題を通じて、親から子への非同期データ渡しの実装を深く学びます。
演習:親から子への非同期データ渡しを実装してみよう
ここでは、親コンポーネントで非同期データを取得し、子コンポーネントに渡すプロセスを実践的に学ぶための演習を行います。この演習を通じて、非同期データの取得、ローディングUI、エラーハンドリング、そして親子間のデータ伝達を実装できるスキルを身につけます。
演習概要
APIから天気データを取得し、親コンポーネントで非同期処理を行い、そのデータを子コンポーネントに渡して表示します。また、ローディング状態やエラー発生時のUIも実装します。
目標
- 親コンポーネントでデータを取得し、子コンポーネントに渡す。
- ローディング中はローディングメッセージを表示する。
- エラーが発生した場合は、適切なエラーメッセージを表示する。
- 子コンポーネントでは、親から渡されたデータを表示する。
演習の手順
以下のコードを参考に実装してください。
親コンポーネント
import React, { useState, useEffect } from 'react';
import WeatherDisplay from './WeatherDisplay';
function WeatherApp() {
const [weatherData, setWeatherData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchWeatherData = async () => {
setLoading(true);
setError(null);
try {
const response = await fetch('https://api.example.com/weather');
if (!response.ok) {
throw new Error('Failed to fetch weather data');
}
const data = await response.json();
setWeatherData(data);
} catch (err) {
setError(err);
} finally {
setLoading(false);
}
};
fetchWeatherData();
}, []);
if (loading) return <p>Loading weather data...</p>;
if (error) return <p>Error: {error.message}</p>;
return <WeatherDisplay data={weatherData} />;
}
export default WeatherApp;
子コンポーネント
import React from 'react';
function WeatherDisplay({ data }) {
return (
<div>
<h2>Weather Information</h2>
<p>City: {data.city}</p>
<p>Temperature: {data.temperature}°C</p>
<p>Condition: {data.condition}</p>
</div>
);
}
export default WeatherDisplay;
課題
- 上記コードを元にアプリケーションを完成させてください。
- 実際に動作を確認し、ローディング中、エラー発生時、データ取得成功時の動作をチェックしてください。
- APIのエンドポイントが変更されても対応できるように、エンドポイントを環境変数として設定してください。
- デザインを改善して、より視覚的に魅力的なローディングUIやエラーメッセージを追加してください。
模範的な応用例
課題を解決した後、以下のような応用機能を実装してみましょう:
- 検索機能: ユーザーが都市名を入力して特定の場所の天気を取得できるようにする。
- ReduxやReact Queryの導入: 状態管理ツールを使って、より複雑なアプリケーションを作成する。
- レスポンシブデザイン: モバイル対応を施して、さまざまなデバイスでの見た目を改善する。
次章では、この記事全体のまとめを行い、Reactで非同期データを扱う際の重要なポイントを総括します。
まとめ
本記事では、Reactにおける非同期データの管理と親から子コンポーネントへのデータ渡しについて、基本から応用まで解説しました。非同期データ取得の基本的な流れやローディングUIの実装、エラーハンドリングの重要性、さらにReduxやReact Queryといった高度なツールの活用方法を学ぶことで、堅牢なReactアプリケーションの構築に必要な知識を習得できたと思います。
特に重要なポイントは以下の通りです:
- 非同期データ取得は親コンポーネントで集中管理し、子コンポーネントには必要なデータだけを渡す設計を採用する。
- ローディング状態やエラーを適切に管理し、ユーザーに分かりやすいフィードバックを提供する。
- アプリケーションの規模や要件に応じて、状態管理ツールやデータフェッチングライブラリを活用する。
これらの知識を活用して、Reactでの非同期データ管理スキルをさらに向上させ、実践的なプロジェクトに役立ててください。
コメント