Reactで配列の中のオブジェクトのプロパティすべてをpropsとして渡す方法を紹介します。
結論は<CartItem key={item.id} {...item} />です。JSXの中でオブジェクトに...を付けると(スプレッド構文)、オブジェクトのプロパティ1つ1つが同名のpropsとして渡されます。受け取る側は引数で({ id, title, price })のように分割代入すれば、必要なものだけ取り出せます。
TypeScriptを使用した場合の書き方、React.memoを使って再レンダリングを防ぐ方法、PropTypesを使う方法、静的データの場合のuseMemo活用なども合わせて紹介します。
Reactで配列の中のオブジェクトのプロパティすべてをpropsとして渡す方法
以下のようなオブジェクトをもった配列を例とします。
const cartItems = [
{
id: 1,
title: "タイトル1",
price: "100",
img: "image-path.jpg",
amount: 1,
},
{
id: 2,
title: "タイトル2",
price: "100",
img: "image-path.jpg",
amount: 1,
},
{
id: 3,
title: "タイトル3",
price: "100",
img: "image-path.jpg",
amount: 1,
},
{
id: 4,
title: "タイトル4",
price: "100",
img: "image-path.jpg",
amount: 1,
},
{
id: 5,
title: "タイトル5",
price: "100",
img: "image-path.jpg",
amount: 1,
},
];
これをpropsとして渡すときいちいちmapメソッドの中でtitle={item.title}などとはしていられません。
次のようにします。
分割代入ですべてpropsとして渡す
import React from "react";
import { useSelector } from "react-redux";
import CartItem from "./CartItem";
const CartContainer = () => {
const { cartItems, amount, total } = useSelector((state) => state.cart);
return (
<div>
{cartItems.map((item) => {
return <CartItem key={item.id} {...item} />;
})}
</div>
);
};
export default CartContainer;
ポイントは{...item}です。
この...はスプレッド構文で、オブジェクトのプロパティをすべて展開して個別のpropsとして渡します。title={item.title} price={item.price}と1つずつ書いたのと同じ結果になります。見出しでは慣例的に「分割代入」と呼んでいますが、正確には渡す側がスプレッド構文、受け取る側が分割代入です。
スプレッド構文はとても便利ですが、万能ではなく、使わないほうがよいケースもあります。
コードの安全性や可読性を高めたい場合は、実際に使用するプロパティ(id, img, title, price, amount)だけを明示的に渡すほうが向いています。
コンポーネントに不要なデータが渡されるのを防ぎ、プロパティの意図を明確にでき、不要な再レンダリングを防ぎやすいからです。
この例のように「オブジェクトのキー名とpropsの名前が一致していて、その全部を使う」場面では、スプレッド構文が簡潔です。JSX内で{}に書けるものの規則はJSX内にJavaScriptの式を埋め込む方法で解説しています。
分割代入ですべてのpropsを受け取る
import React from "react";
const CartItem = ({ id, img, title, price, amount }) => {
return (
<article className="cart-item">
<img src={img} alt={title} />
<div>
<h2>{id}: {title}</h2>
<h4>¥{price}</h4>
</div>
</article>
);
};
export default CartItem;
型安全性を強化(TypeScriptの使用)
Reactで配列のオブジェクトをpropsとして渡す場合、TypeScriptを使用して型定義を行うと、型安全性が向上します。
type CartItemProps = {
id: number;
img: string;
title: string;
price: string;
amount: number;
};
const CartItem: React.FC<CartItemProps> = ({ id, img, title, price, amount }) => {
return (
<article className="cart-item">
<img src={img} alt={title} />
<div>
<h2>{id}: {title}</h2>
<h4>¥{price}</h4>
</div>
</article>
);
};
型定義を行うことで、渡されるpropsの型チェックが可能になります。
開発中にプロパティのミス(タイプミスや不足)を防げます。
なおReact.FCを使わず、const CartItem = ({ id, img, title, price, amount }: CartItemProps) => { ... }と引数に直接型を付ける書き方も一般的です。どちらでも動作は変わりません。子要素を受け取るコンポーネントの型付けはpropsのchildrenの使い方を参照してください。
パフォーマンス最適化:React.memoの利用
CartItemが受け取るpropsが頻繁に変化しない場合、React.memoを使って再レンダリングを防げます。
import React, { memo } from 'react';
const CartItem = memo(({ id, img, title, price, amount }) => {
return (
<article className="cart-item">
<img src={img} alt={title} />
<div>
<h2>{id}: {title}</h2>
<h4>¥{price}</h4>
</div>
</article>
);
});
export default CartItem;
親コンポーネント(CartContainer)が再レンダリングされても、子コンポーネント(CartItem)が無駄に再レンダリングされなくなります。
パフォーマンスの向上が期待できます。
PropTypesを使う方法
CartItemのプロパティが全て渡されることを確認するため、PropTypesを使う方法もあります(TypeScript未使用の場合)。
ただし、React 19では関数コンポーネントのpropTypesチェックが削除され、設定しても無視されます。React 18以前のプロジェクト向けの方法と考え、React 19以降で型チェックをしたい場合はTypeScriptを使ってください。
import PropTypes from 'prop-types';
const CartItem = ({ id, img, title, price, amount }) => {
return (
<article className="cart-item">
<img src={img} alt={title} />
<div>
<h2>{id}: {title}</h2>
<h4>¥{price}</h4>
</div>
</article>
);
};
CartItem.propTypes = {
id: PropTypes.number.isRequired,
img: PropTypes.string.isRequired,
title: PropTypes.string.isRequired,
price: PropTypes.string.isRequired,
amount: PropTypes.number.isRequired,
};
export default CartItem;
TypeScriptを使わないプロジェクトでも、最低限の型チェックを行えます。
静的データの場合のuseMemo活用
import React, { useMemo } from 'react';
const CartContainer = () => {
const { cartItems } = useSelector((state) => state.cart);
const memoizedCartItems = useMemo(() => {
return cartItems.map(({ id, img, title, price, amount }) => (
<CartItem
key={id}
id={id}
img={img}
title={title}
price={price}
amount={amount}
/>
));
}, [cartItems]);
return <div>{memoizedCartItems}</div>;
};
export default CartContainer;
cartItemsが変更されない限り、新しい配列を生成せずに済むため、パフォーマンスが向上します。