Reactのpropsとは?親子コンポーネントのデータ受け渡し入門

Reactのpropsの基本概念から実践的な使い方まで詳しく解説。親子コンポーネント間でのデータ受け渡しの仕組みと活用方法を初心者向けに紹介

Learning Next 運営
25 分で読めます

みなさん、Reactを学び始めたばかりで「propsって何?」と思ったことはありませんか?

「親コンポーネントから子コンポーネントへのデータ受け渡し」って聞いても、最初はピンと来ないですよね。 でも大丈夫です!propsはReactの基本中の基本で、理解すれば必ず使いこなせるようになります。

この記事では、Reactのpropsについて基本的な概念から実践的な使い方まで、初心者の方にも分かりやすくお伝えします。 具体的なコード例をたくさん使って、親子コンポーネント間でのデータ受け渡しの仕組みを一緒に学んでいきましょう。

propsって何?基本的な考え方

propsは「properties(プロパティ)」の略です。

簡単に言うと、コンポーネント同士で情報をやり取りするための仕組みなんです。

身近な例で理解してみよう

propsの概念を、身近な例で考えてみましょう。

例えば、お母さんが子どもにおやつを渡すとき。 お母さんが親、子どもが子、おやつがpropsです。

// 親コンポーネント(お母さん)
function App() {
  return (
    <div>
      <Welcome name="太郎" />
      <Welcome name="花子" />
    </div>
  );
}

// 子コンポーネント(子ども)
function Welcome(props) {
  return <h1>こんにちは、{props.name}さん!</h1>;
}

この例では、nameというpropsを使って、親コンポーネント(App)から子コンポーネント(Welcome)に名前を渡しています。

実行すると、こんな結果になります。

こんにちは、太郎さん!
こんにちは、花子さん!

親から子へ、一方通行で情報が流れているのがポイントです。

propsの重要な特徴

propsには、覚えておきたい特徴があります。

function Welcome(props) {
  // ❌ これはエラーになる(propsは変更できない)
  // props.name = "新しい名前";
  
  // ✅ 読み取りのみ可能
  return <h1>こんにちは、{props.name}さん!</h1>;
}

propsは読み取り専用です。 子コンポーネントが受け取ったpropsを直接変更することはできません。

この制限があることで、データの流れが予測しやすくなり、バグの少ないアプリを作れるんです。

もう一つの特徴は、上から下への一方向の流れです。

function Parent() {
  const userName = "田中太郎";
  
  return (
    <div>
      <Child name={userName} />
    </div>
  );
}

function Child(props) {
  return <p>受け取った名前: {props.name}</p>;
}

親コンポーネントから子コンポーネントへ、常に上から下に情報が流れます。

propsの基本的な使い方

様々な種類のデータをpropsで渡してみましょう。

どんなデータでも渡せるのが、propsの便利なところです。

文字列を渡す場合

文字列は一番シンプルなpropsです。

function App() {
  return (
    <div>
      <UserProfile name="田中太郎" />
      <UserProfile name="佐藤花子" />
    </div>
  );
}

function UserProfile({ name }) {
  return (
    <div>
      <h2>ユーザープロフィール</h2>
      <p>名前: {name}</p>
    </div>
  );
}

ここでは{ name }という書き方を使っています。 これは「分割代入」という方法で、propsから直接値を取り出せて便利です。

数値を渡す場合

数値を渡すときは、{}で囲みます。

function App() {
  return (
    <div>
      <Counter initialValue={0} />
      <Counter initialValue={10} />
    </div>
  );
}

function Counter({ initialValue }) {
  const [count, setCount] = useState(initialValue);
  
  return (
    <div>
      <p>カウント: {count}</p>
      <button onClick={() => setCount(count + 1)}>
        +1
      </button>
    </div>
  );
}

この例では、カウンターの初期値を親から子に渡しています。 一つは0から、もう一つは10からカウントが始まります。

真偽値(true/false)を渡す場合

ボタンの見た目を変える例を見てみましょう。

function App() {
  return (
    <div>
      <Button isPrimary={true} text="メインボタン" />
      <Button isPrimary={false} text="サブボタン" />
    </div>
  );
}

function Button({ isPrimary, text }) {
  return (
    <button className={isPrimary ? 'btn-primary' : 'btn-secondary'}>
      {text}
    </button>
  );
}

isPrimaryの値によって、ボタンのクラス名が変わります。 これで、見た目の違うボタンを作り分けられます。

配列やオブジェクトを渡す場合

もっと複雑なデータも渡せます。

function App() {
  const user = {
    name: '田中太郎',
    age: 25,
    email: 'tanaka@example.com'
  };
  
  const hobbies = ['読書', '映画鑑賞', 'プログラミング'];
  
  return (
    <div>
      <UserCard user={user} hobbies={hobbies} />
    </div>
  );
}

function UserCard({ user, hobbies }) {
  return (
    <div>
      <h2>{user.name}</h2>
      <p>年齢: {user.age}歳</p>
      <p>メール: {user.email}</p>
      <div>
        <h3>趣味</h3>
        <ul>
          {hobbies.map((hobby, index) => (
            <li key={index}>{hobby}</li>
          ))}
        </ul>
      </div>
    </div>
  );
}

オブジェクトや配列を渡すことで、まとまった情報を一度に子コンポーネントに渡せます。

hobbies.map()で配列の中身を一つずつ表示しています。 key={index}は、Reactが効率よく画面を更新するために必要な属性です。

propsの受け取り方のパターン

propsを受け取る方法は何通りかあります。

どの方法を選ぶかで、コードの読みやすさが変わってきます。

基本的な受け取り方

最初は、この3つのパターンを覚えておきましょう。

// 方法1: propsオブジェクトとして受け取る
function Welcome(props) {
  return <h1>こんにちは、{props.name}さん!</h1>;
}

// 方法2: 分割代入で受け取る(おすすめ)
function Welcome({ name }) {
  return <h1>こんにちは、{name}さん!</h1>;
}

// 方法3: 複数のpropsを分割代入で受け取る
function UserProfile({ name, age, email }) {
  return (
    <div>
      <h2>{name}</h2>
      <p>年齢: {age}歳</p>
      <p>メール: {email}</p>
    </div>
  );
}

方法2の分割代入がおすすめです。 コードがスッキリして、どのpropsを使っているかが一目で分かります。

デフォルト値を設定する

propsが渡されなかった場合の初期値を設定できます。

// 方法1: 関数の引数でデフォルト値を設定
function Welcome({ name = '匿名ユーザー' }) {
  return <h1>こんにちは、{name}さん!</h1>;
}

// 方法2: defaultPropsを使用
function Welcome({ name }) {
  return <h1>こんにちは、{name}さん!</h1>;
}

Welcome.defaultProps = {
  name: '匿名ユーザー'
};

どちらでも同じ結果になります。

function App() {
  return (
    <div>
      <Welcome name="田中太郎" /> {/* 田中太郎 */}
      <Welcome />                  {/* 匿名ユーザー */}
    </div>
  );
}

propsが渡されなかった場合は、デフォルト値が使われます。

残りのpropsをまとめて受け取る

...を使って、指定していないpropsをまとめて受け取ることもできます。

function Button({ variant, children, ...otherProps }) {
  return (
    <button 
      className={`btn btn-${variant}`}
      {...otherProps}
    >
      {children}
    </button>
  );
}

使用例はこんな感じです。

function App() {
  return (
    <div>
      <Button 
        variant="primary"
        onClick={() => console.log('クリック')}
        disabled={false}
      >
        ボタンのテキスト
      </Button>
    </div>
  );
}

variantchildren以外の属性(onClickdisabledなど)は、otherPropsにまとめられて、そのまま<button>要素に渡されます。

関数をpropsとして渡そう

propsでは、関数も渡すことができます。

これを使うと、子コンポーネントから親コンポーネントにデータを送ることができるんです。

基本的な例

まずは、シンプルな例から見てみましょう。

function App() {
  const [message, setMessage] = useState('');
  
  const handleButtonClick = (text) => {
    setMessage(text);
  };
  
  return (
    <div>
      <p>メッセージ: {message}</p>
      <ChildComponent onButtonClick={handleButtonClick} />
    </div>
  );
}

function ChildComponent({ onButtonClick }) {
  return (
    <div>
      <button onClick={() => onButtonClick('こんにちは')}>
        挨拶
      </button>
      <button onClick={() => onButtonClick('さようなら')}>
        お別れ
      </button>
    </div>
  );
}

この仕組みを詳しく見てみましょう。

  1. 親コンポーネント(App)でhandleButtonClick関数を定義
  2. その関数をonButtonClickというpropsとして子コンポーネントに渡す
  3. 子コンポーネントでボタンがクリックされたら、受け取った関数を実行
  4. 親コンポーネントの状態(message)が更新される

これで、子から親へデータを送ることができました!

フォームの実践例

もう少し実践的な例として、ユーザー登録フォームを作ってみましょう。

function App() {
  const [users, setUsers] = useState([]);
  
  const handleAddUser = (newUser) => {
    setUsers(prevUsers => [...prevUsers, newUser]);
  };
  
  return (
    <div>
      <UserForm onAddUser={handleAddUser} />
      <UserList users={users} />
    </div>
  );
}

まず、親コンポーネントの全体像です。 usersという配列でユーザー一覧を管理しています。

次に、フォームコンポーネントを見てみましょう。

function UserForm({ onAddUser }) {
  const [name, setName] = useState('');
  const [email, setEmail] = useState('');
  
  const handleSubmit = (e) => {
    e.preventDefault();
    onAddUser({ name, email });
    setName('');
    setEmail('');
  };
  
  return (
    <form onSubmit={handleSubmit}>
      <input 
        type="text"
        value={name}
        onChange={(e) => setName(e.target.value)}
        placeholder="名前"
      />
      <input 
        type="email"
        value={email}
        onChange={(e) => setEmail(e.target.value)}
        placeholder="メールアドレス"
      />
      <button type="submit">追加</button>
    </form>
  );
}

フォームが送信されたとき、onAddUser関数を呼び出して新しいユーザー情報を親に送ります。

最後に、ユーザー一覧を表示するコンポーネントです。

function UserList({ users }) {
  return (
    <ul>
      {users.map((user, index) => (
        <li key={index}>{user.name} - {user.email}</li>
      ))}
    </ul>
  );
}

これで、フォームで入力したユーザー情報が一覧に追加される仕組みの完成です!

実践的なpropsの活用例

実際のアプリ開発でよく使われるパターンを見てみましょう。

これらの例を参考にすると、より実用的なコンポーネントが作れるようになります。

商品リストの作成

ECサイトでよくある商品リストを作ってみましょう。

まずは、全体の構造をご覧ください。

function App() {
  const products = [
    { id: 1, name: 'ノートPC', price: 80000, inStock: true },
    { id: 2, name: 'マウス', price: 3000, inStock: false },
    { id: 3, name: 'キーボード', price: 8000, inStock: true }
  ];
  
  const handleAddToCart = (productId) => {
    console.log(`商品ID: ${productId}をカートに追加`);
  };
  
  return (
    <div>
      <h1>商品一覧</h1>
      <ProductList 
        products={products} 
        onAddToCart={handleAddToCart}
      />
    </div>
  );
}

商品データを配列で管理して、カートに追加する関数も用意しています。

次に、商品リストコンポーネントです。

function ProductList({ products, onAddToCart }) {
  return (
    <div className="product-list">
      {products.map(product => (
        <ProductCard 
          key={product.id}
          product={product}
          onAddToCart={onAddToCart}
        />
      ))}
    </div>
  );
}

products配列をループして、一つずつProductCardコンポーネントに渡しています。 key={product.id}で、各商品を識別しています。

最後に、個別の商品カードコンポーネントです。

function ProductCard({ product, onAddToCart }) {
  const { id, name, price, inStock } = product;
  
  return (
    <div className="product-card">
      <h3>{name}</h3>
      <p>価格: ¥{price.toLocaleString()}</p>
      <p>在庫: {inStock ? '有り' : '無し'}</p>
      <button 
        onClick={() => onAddToCart(id)}
        disabled={!inStock}
      >
        {inStock ? 'カートに追加' : '在庫切れ'}
      </button>
    </div>
  );
}

商品オブジェクトから必要な情報を取り出して表示しています。 在庫がない場合は、ボタンを無効化するのもポイントです。

条件によって表示を変える例

ログイン状態によって画面を切り替える例も見てみましょう。

function App() {
  const [user, setUser] = useState(null);
  
  const handleLogin = (userData) => {
    setUser(userData);
  };
  
  const handleLogout = () => {
    setUser(null);
  };
  
  return (
    <div>
      <Header user={user} onLogout={handleLogout} />
      <main>
        {user ? (
          <Dashboard user={user} />
        ) : (
          <LoginForm onLogin={handleLogin} />
        )}
      </main>
    </div>
  );
}

userの状態によって、ダッシュボードかログインフォームかを切り替えています。

ヘッダーコンポーネントでは、ユーザー情報の表示を制御します。

function Header({ user, onLogout }) {
  return (
    <header>
      <h1>マイアプリ</h1>
      {user ? (
        <div>
          <span>ようこそ、{user.name}さん</span>
          <button onClick={onLogout}>ログアウト</button>
        </div>
      ) : (
        <span>ログインしてください</span>
      )}
    </header>
  );
}

ダッシュボードでは、さらに細かいコンポーネントに分けています。

function Dashboard({ user }) {
  return (
    <div>
      <h2>ダッシュボード</h2>
      <UserProfile user={user} />
      <RecentActivity userId={user.id} />
    </div>
  );
}

function UserProfile({ user }) {
  return (
    <div>
      <h3>プロフィール</h3>
      <p>名前: {user.name}</p>
      <p>メール: {user.email}</p>
    </div>
  );
}

このように、propsを使ってコンポーネントを細かく分けることで、再利用しやすいコードになります。

propsを使う時の注意点

propsを使う際に気をつけるべきポイントをご紹介します。

これらを意識すると、より良いコードが書けるようになります。

分かりやすい命名をしよう

propsの名前は、見ただけで何を表すかが分かるものにしましょう。

// ✅ 良い例:分かりやすい名前
function UserCard({ userName, userAge, isActive }) {
  return (
    <div>
      <p>名前: {userName}</p>
      <p>年齢: {userAge}歳</p>
      <p>状態: {isActive ? 'アクティブ' : '非アクティブ'}</p>
    </div>
  );
}

// ❌ 避けるべき例:意味が分からない名前
function UserCard({ a, b, c }) {
  return (
    <div>
      <p>名前: {a}</p>
      <p>年齢: {b}歳</p>
      <p>状態: {c ? 'アクティブ' : '非アクティブ'}</p>
    </div>
  );
}

後から見直したときに、何を表しているかがすぐに分かる名前をつけましょう。

propsが多すぎる場合は整理しよう

propsの数が多くなりすぎたら、オブジェクトにまとめることを検討してみてください。

// ❌ プロパティが多すぎる場合
function UserProfile({ 
  name, age, email, phone, address, city, country, 
  occupation, company, salary, hobbies, skills 
}) {
  // 複雑になりがち
}

// ✅ オブジェクトにまとめる
function UserProfile({ user, contact, employment }) {
  return (
    <div>
      <h2>{user.name}</h2>
      <p>年齢: {user.age}歳</p>
      <p>メール: {contact.email}</p>
      <p>電話: {contact.phone}</p>
      <p>職業: {employment.occupation}</p>
      <p>会社: {employment.company}</p>
    </div>
  );
}

関連する情報をグループ化することで、コードが読みやすくなります。

必要ないpropsは渡さない

すべてのコンポーネントに同じpropsを渡す必要はありません。

// ❌ 不必要にpropsを渡す
function App() {
  const appTitle = "マイアプリ";
  
  return (
    <div>
      <Header title={appTitle} />
      <Main title={appTitle} />
      <Footer title={appTitle} />
    </div>
  );
}

// ✅ 必要な場所でのみpropsを使用
function App() {
  const appTitle = "マイアプリ";
  
  return (
    <div>
      <Header title={appTitle} />
      <Main />
      <Footer />
    </div>
  );
}

実際にタイトルを使うコンポーネントにだけ、propsを渡すようにしましょう。

まとめ

Reactのpropsは、コンポーネント間でデータをやり取りするための重要な仕組みです。

親から子への一方向の流れを理解することで、アプリケーションの構造が明確になり、予測しやすいコードが書けるようになります。

覚えておきたい重要なポイントはこちらです。

  • propsは読み取り専用で、子コンポーネントが直接変更することはできない
  • 分割代入を使って受け取ると、コードがスッキリする
  • 関数をpropsとして渡すことで、子から親にデータを送ることができる
  • propsの名前は分かりやすくつけて、必要な場所でのみ使用する

最初は複雑に感じるかもしれませんが、実際にコードを書いて練習することで必ず理解できます。

ぜひ今回の例を参考にして、自分なりのコンポーネントを作ってpropsの活用に慣れてみてください。 きっと、Reactの便利さを実感できるはずです!

関連記事