レッスン 4
最初のステップとして、商品カードを別ファイルに分割しましょう。 現在App.jsxに書かれている商品カードのコードを、新しく作るProductCard.jsxファイルに移動させます。 手順: 1. 下の「ProductCard.jsxを作成」ボタンを押して空のファイルを作成する 2. ProductCardコンポーネントとしてexport defaultする 3. App.jsxから商品カードに関連するコードをコピーする 4. App.jsxでProductCardをimportして使用する
import './styles.css'
const App = () => {
const getStarRating = (rating) => {
return '★'.repeat(rating) + '☆'.repeat(5 - rating);
}
const product = {
name: "スマートウォッチ",
brand: "TechGear",
price: 12000,
discountRate: 0.20,
rating: 4,
reviewCount: 128,
imageUrl: "https://images.unsplash.com/photo-1523275335684-37898b6baf30?w=400&h=533&fit=crop&crop=center",
altText: "スマートウォッチの商品画像"
};
const discountPercent = Math.round(product.discountRate * 100);
const discountedPrice = product.price - product.price * product.discountRate;
return (
<div className="product-card">
<div className="product-image-container">
<img
className="product-image"
src={product.imageUrl}
alt={product.altText}
/>
</div>
<div className="product-info">
<p className="brand-name">{product.brand}</p>
<h1>{product.name}</h1>
<div className="price-container">
<p className="discount-price">¥{discountedPrice.toLocaleString()}</p>
<p className="price">¥{product.price.toLocaleString()}</p>
<span className="discount-badge">{discountPercent}%OFF</span>
</div>
<p className="rating">
{getStarRating(product.rating)}
<span className="review-count">({product.reviewCount})</span>
</p>
</div>
</div>
)
}
export default Appimport './styles.css'
const ProductCard = () => {
// ここに商品カードのロジックを移動
return (
// ここに商品カードのJSXを移動
)
}
export default ProductCardconst getStarRating = (rating) => {
return '★'.repeat(rating) + '☆'.repeat(5 - rating);
}
const product = {
name: "スマートウォッチ",
brand: "TechGear",
price: 12000,
discountRate: 0.20,
rating: 4,
reviewCount: 128,
imageUrl: "https://images.unsplash.com/photo-1523275335684-37898b6baf30?w=400&h=533&fit=crop&crop=center",
altText: "スマートウォッチの商品画像"
};
const discountPercent = Math.round(product.discountRate * 100);
const discountedPrice = product.price - product.price * product.discountRate;return (
<div className="product-card">
<div className="product-image-container">
<img
className="product-image"
src={product.imageUrl}
alt={product.altText}
/>
</div>
<div className="product-info">
<p className="brand-name">{product.brand}</p>
<h1>{product.name}</h1>
<div className="price-container">
<p className="discount-price">¥{discountedPrice.toLocaleString()}</p>
<p className="price">¥{product.price.toLocaleString()}</p>
<span className="discount-badge">{discountPercent}%OFF</span>
</div>
<p className="rating">
{getStarRating(product.rating)}
<span className="review-count">({product.reviewCount})</span>
</p>
</div>
</div>
)import ProductCard from './ProductCard'
const App = () => {
return (
<div>
<ProductCard />
</div>
)
}
export default App次に、商品画像部分を別コンポーネントに分割します。 ProductImage.jsxファイルを作成しましょう。 実装する内容: - 商品画像を表示するProductImageコンポーネント - ProductCard.jsxから呼び出す - 画像関連のスタイルも適用
import './styles.css'
const ProductCard = () => {
const getStarRating = (rating) => {
return '★'.repeat(rating) + '☆'.repeat(5 - rating);
}
const product = {
name: "スマートウォッチ",
brand: "TechGear",
price: 12000,
discountRate: 0.20,
rating: 4,
reviewCount: 128,
imageUrl: "https://images.unsplash.com/photo-1523275335684-37898b6baf30?w=400&h=533&fit=crop&crop=center",
altText: "スマートウォッチの商品画像"
};
const discountPercent = Math.round(product.discountRate * 100);
const discountedPrice = product.price - product.price * product.discountRate;
return (
<div className="product-card">
<div className="product-image-container">
<img
className="product-image"
src={product.imageUrl}
alt={product.altText}
/>
</div>
<div className="product-info">
<p className="brand-name">{product.brand}</p>
<h1>{product.name}</h1>
<div className="price-container">
<p className="discount-price">¥{discountedPrice.toLocaleString()}</p>
<p className="price">¥{product.price.toLocaleString()}</p>
<span className="discount-badge">{discountPercent}%OFF</span>
</div>
<p className="rating">
{getStarRating(product.rating)}
<span className="review-count">({product.reviewCount})</span>
</p>
</div>
</div>
)
}
export default ProductCardconst ProductImage = () => {
const product = {
imageUrl: "https://images.unsplash.com/photo-1523275335684-37898b6baf30?w=400&h=533&fit=crop&crop=center",
altText: "スマートウォッチの商品画像"
};
return (
<div className="product-image-container">
<img
className="product-image"
src={product.imageUrl}
alt={product.altText}
/>
</div>
);
}
export default ProductImageimport ProductImage from './ProductImage'<ProductImage />次に、商品情報部分を別コンポーネントに分割します。 ProductInfo.jsxファイルを作成して、商品情報表示の責任を持たせましょう。 手順: 1. 下の「ProductInfo.jsxを作成」ボタンを押して空のファイルを作成する 2. ProductInfoコンポーネントとしてexport defaultする 3. ProductCard.jsxから商品情報に関連するコードをコピーする 4. getStarRating関数もProductInfo内に移動する 5. ProductCard.jsxでProductInfoをimportして使用する
import './styles.css'
import ProductImage from './ProductImage'
const ProductCard = () => {
const getStarRating = (rating) => {
return '★'.repeat(rating) + '☆'.repeat(5 - rating);
}
const product = {
name: "スマートウォッチ",
brand: "TechGear",
price: 12000,
discountRate: 0.20,
rating: 4,
reviewCount: 128
};
const discountPercent = Math.round(product.discountRate * 100);
const discountedPrice = product.price - product.price * product.discountRate;
return (
<div className="product-card">
<ProductImage />
<div className="product-info">
<p className="brand-name">{product.brand}</p>
<h1>{product.name}</h1>
<div className="price-container">
<p className="discount-price">¥{discountedPrice.toLocaleString()}</p>
<p className="price">¥{product.price.toLocaleString()}</p>
<span className="discount-badge">{discountPercent}%OFF</span>
</div>
<p className="rating">
{getStarRating(product.rating)}
<span className="review-count">({product.reviewCount})</span>
</p>
</div>
</div>
)
}
export default ProductCardconst ProductInfo = () => {
const getStarRating = (rating) => {
return '★'.repeat(rating) + '☆'.repeat(5 - rating);
}
const product = {
name: "スマートウォッチ",
brand: "TechGear",
price: 12000,
discountRate: 0.20,
rating: 4,
reviewCount: 128
};
const discountPercent = Math.round(product.discountRate * 100);
const discountedPrice = product.price - product.price * product.discountRate;
return (
<div className="product-info">
<p className="brand-name">{product.brand}</p>
<h1>{product.name}</h1>
<div className="price-container">
<p className="discount-price">¥{discountedPrice.toLocaleString()}</p>
<p className="price">¥{product.price.toLocaleString()}</p>
<span className="discount-badge">{discountPercent}%OFF</span>
</div>
<p className="rating">
{getStarRating(product.rating)}
<span className="review-count">({product.reviewCount})</span>
</p>
</div>
);
}
export default ProductInfoimport './styles.css'
import ProductImage from './ProductImage'
import ProductInfo from './ProductInfo'
const ProductCard = () => {
return (
<div className="product-card">
<ProductImage />
<ProductInfo />
</div>
)
}
export default ProductCardコンポーネント分割ができました。次は、各コンポーネントにハードコーディングされているデータをProps(プロパティ)を使って外部から渡すように改善しましょう。 Propsを使うことで: - コンポーネントの再利用性が向上 - データの一元管理ができる - 異なる商品データで同じコンポーネントを使いまわせる 実装する内容: - App.jsxで商品データを定義 - ProductCard → ProductImage, ProductInfoへpropsでデータを渡す - 各コンポーネントのハードコーディングされたデータを削除
import ProductCard from './ProductCard'
import './styles.css'
const App = () => {
// 4つの異なる商品データを定義
const products = [
{
id: 1,
name: "スマートウォッチ",
brand: "TechGear",
price: 12000,
discountRate: 0.20,
rating: 4,
reviewCount: 128,
imageUrl: "https://images.unsplash.com/photo-1523275335684-37898b6baf30?w=400&h=533&fit=crop&crop=center",
altText: "スマートウォッチの商品画像"
},
{
id: 2,
name: "ワイヤレスイヤホン",
brand: "SoundMax",
price: 8000,
discountRate: 0.15,
rating: 5,
reviewCount: 95,
imageUrl: "https://images.unsplash.com/photo-1505740420928-5e560c06d30e?w=400&h=533&fit=crop&crop=center",
altText: "ワイヤレスイヤホンの商品画像"
},
{
id: 3,
name: "デジタルカメラ",
brand: "PhotoMax",
price: 28000,
discountRate: 0.15,
rating: 5,
reviewCount: 89,
imageUrl: "https://images.unsplash.com/photo-1516035069371-29a1b244cc32?w=400&h=533&fit=crop&crop=center",
altText: "デジタルカメラの商品画像"
},
{
id: 4,
name: "Bluetoothスピーカー",
brand: "AudioWave",
price: 15000,
discountRate: 0.25,
rating: 5,
reviewCount: 203,
imageUrl: "https://images.unsplash.com/photo-1608043152269-423dbba4e7e1?w=400&h=533&fit=crop&crop=center",
altText: "Bluetoothスピーカーの商品画像"
}
];
return (
<div className="app-container">
<p className="category-path">All › 電化製品 › スマートデバイス</p>
<div className="product-grid">
{products.map(product => (
<ProductCard key={product.id} product={product} />
))}
</div>
</div>
)
}
export default Appimport './styles.css'
import ProductImage from './ProductImage'
import ProductInfo from './ProductInfo'
const ProductCard = ({ product }) => {
return (
<div className="product-card">
<ProductImage product={product} />
<ProductInfo product={product} />
</div>
)
}
export default ProductCardconst ProductImage = ({ product }) => {
return (
<div className="product-image-container">
<img
className="product-image"
src={product.imageUrl}
alt={product.altText}
/>
</div>
);
}
export default ProductImageconst ProductInfo = ({ product }) => {
const getStarRating = (rating) => {
return '★'.repeat(rating) + '☆'.repeat(5 - rating);
}
const discountPercent = Math.round(product.discountRate * 100);
const discountedPrice = product.price - product.price * product.discountRate;
return (
<div className="product-info">
<p className="brand-name">{product.brand}</p>
<h1>{product.name}</h1>
<div className="price-container">
<p className="discount-price">¥{discountedPrice.toLocaleString()}</p>
<p className="price">¥{product.price.toLocaleString()}</p>
<span className="discount-badge">{discountPercent}%OFF</span>
</div>
<p className="rating">
{getStarRating(product.rating)}
<span className="review-count">({product.reviewCount})</span>
</p>
</div>
);
}
export default ProductInfoお疲れ様でした!次のレッスンへ進みましょう!