はじめに
第4章でReactとバックエンドを連携しました。今回は、アプリが複雑になるにつれて必要となる「状態管理」を強化します。具体的には、Context APIを使って商品リストをグローバルに管理する方法を学びます。
目標
- Context APIの基本を理解する
- グローバルな状態を管理する
- 商品追加機能を追加する
Context APIとは
Context APIは、Reactでコンポーネント間でデータを簡単に共有するための仕組みです。Propsを何層も渡す必要がなくなり、コードがシンプルになります。
Contextの作成
frontend/src
にProductContext.tsx
を作成し、以下を追加:
import React, { createContext, useState, useContext } from 'react';
const ProductContext = createContext();
export const ProductProvider = ({ children }) => {
const [products, setProducts] = useState([]);
const addProduct = (newProduct) => {
setProducts((prev) => [...prev, { ...newProduct, id: Date.now() }]);
};
return (
<ProductContext.Provider value={{ products, setProducts, addProduct }}>
{children}
</ProductContext.Provider>
);
};
export const useProductContext = () => useContext(ProductContext);
Appコンポーネントの更新
App.tsx
を以下のように修正し、Contextを利用:
import React, { useEffect } from 'react';
import './App.css';
import { ProductProvider, useProductContext } from './ProductContext';
function App() {
return (
<ProductProvider>
<div className="App">
<Header />
<ProductList />
<Footer />
</div>
</ProductProvider>
);
}
const Header = () => (
<header>
<h1>商品リスト</h1>
</header>
);
const ProductList = () => {
const { products, setProducts, addProduct } = useProductContext();
const [loading, setLoading] = React.useState(true);
const [error, setError] = React.useState(null);
const [newProduct, setNewProduct] = React.useState({ name: '', price: '' });
useEffect(() => {
const fetchProducts = async () => {
try {
const response = await fetch('http://localhost:5000/products');
if (!response.ok) throw new Error('データの取得に失敗しました');
const data = await response.json();
setProducts(data);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
fetchProducts();
}, [setProducts]);
const handleAddProduct = (e) => {
e.preventDefault();
if (newProduct.name && newProduct.price) {
addProduct({ name: newProduct.name, price: Number(newProduct.price) });
setNewProduct({ name: '', price: '' });
}
};
if (loading) return <main>読み込み中...</main>;
if (error) return <main>エラー: {error}</main>;
return (
<main>
<h2>商品一覧</h2>
<form onSubmit={handleAddProduct}>
<input
type="text"
placeholder="商品名"
value={newProduct.name}
onChange={(e) => setNewProduct({ ...newProduct, name: e.target.value })}
/>
<input
type="number"
placeholder="価格"
value={newProduct.price}
onChange={(e) => setNewProduct({ ...newProduct, price: e.target.value })}
/>
<button type="submit">追加</button>
</form>
<ul>
{products.map((product) => (
<ProductItem key={product.id} name={product.name} price={product.price} />
))}
</ul>
</main>
);
};
const ProductItem = ({ name, price }) => (
<li>
{name} - {price}円
</li>
);
const Footer = () => (
<footer>
<p>© 2025 React Web開発入門</p>
</footer>
);
export default App;
CSSの調整
App.css
にフォーム用のスタイルを追加:
form {
margin: 20px 0;
}
input {
padding: 5px;
margin-right: 10px;
}
button {
padding: 5px 10px;
background-color: #61dafb;
border: none;
cursor: pointer;
}
button:hover {
background-color: #21a1f1;
}
動作確認
- バックエンドを起動(
node index.js
)。 - フロントエンドを起動(
npm start
)。 -
http://localhost:3000
で確認。商品リストが表示され、フォームで新しい商品を追加できます。
- 注意: 今回は追加した商品はバックエンドに保存されません(次章で対応)。
次回予告
第6章では、バックエンドにデータベースを追加し、商品データを永続的に保存する方法を学びます。お楽しみに!
この記事が役に立ったら、ぜひ「いいね」や「ストック」をお願いします!質問や感想はコメント欄で気軽にどうぞ。次回も一緒に学びましょう!