ReactにおけるPropsとStateの管理

2025-08-07

Reactコンポーネントの核心概念であるProps(プロパティ)とState(状態)について、初心者向けに徹底解説します。これらの概念を理解することで、動的でインタラクティブなアプリケーションを構築できるようになります。

PropsとStateの基本理解

Propsとは?

Props(プロパティ)は親コンポーネントから子コンポーネントにデータを渡すための仕組みです。以下の特徴があります:

  • 読み取り専用:子コンポーネントは受け取ったPropsを変更できない
  • 単方向データフロー:親から子への一方通行
  • 任意のデータ型:文字列、数値、オブジェクト、関数など
// 親コンポーネント
function ParentComponent() {
  return <ChildComponent name="山田太郎" age={25} />;
}

// 子コンポーネント
function ChildComponent(props) {
  return (
    <div>
      <p>名前: {props.name}</p>
      <p>年齢: {props.age}</p>
    </div>
  );
}

Stateとは?

Stateはコンポーネント内部で管理される状態データです。以下の特徴があります:

  • コンポーネント固有:各コンポーネントが独自のStateを持つ
  • 変更可能:setStateやuseStateフックで更新可能
  • 再レンダリングトリガー:Stateが変更されるとコンポーネントが再描画
import { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0); // Stateの宣言

  return (
    <div>
      <p>現在のカウント: {count}</p>
      <button onClick={() => setCount(count + 1)>増やす</button>
    </div>
  );
}

Propsの詳細解説

Propsの受け渡し方法

関数型コンポーネントでのProps

// 方法1: propsオブジェクトを使用
function Welcome(props) {
  return <h1>こんにちは、{props.name}さん!</h1>;
}

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

// 使用例
<Welcome name="佐藤花子" />

クラス型コンポーネントでのProps

class Welcome extends React.Component {
  render() {
    return <h1>こんにちは、{this.props.name}さん!</h1>;
  }
}

// 使用例
<Welcome name="田中一郎" />

デフォルトPropsの設定

Propsが渡されなかった場合のデフォルト値を設定できます。

function Welcome({ name }) {
  return <h1>こんにちは、{name}さん!</h1>;
}

Welcome.defaultProps = {
  name: 'ゲスト'
};

// nameが未指定の場合
<Welcome /> // "こんにちは、ゲストさん!"と表示

子供要素としてのProps(children)

コンポーネントタグの間に記述した内容はchildrenとして渡されます。

function Card({ children }) {
  return <div className="card">{children}</div>;
}

// 使用例
<Card>
  <h2>タイトル</h2>
  <p>コンテンツ</p>
</Card>

Propsの型チェック(PropTypes)

大規模アプリケーションではPropsの型をチェックすると良いでしょう。

import PropTypes from 'prop-types';

function UserProfile({ name, age, hobbies }) {
  // ...
}

UserProfile.propTypes = {
  name: PropTypes.string.isRequired,
  age: PropTypes.number,
  hobbies: PropTypes.arrayOf(PropTypes.string)
};

UserProfile.defaultProps = {
  age: 20,
  hobbies: []
};

Stateの詳細解説

関数型コンポーネントでのState(useStateフック)

React 16.8で導入されたHooks APIを使用します。

import { useState } from 'react';

function Counter() {
  // count: 現在の状態値
  // setCount: 状態を更新する関数
  const [count, setCount] = useState(0); // 初期値0

  return (
    <div>
      <p>カウント: {count}</p>
      <button onClick={() => setCount(count + 1)>増やす</button>
      <button onClick={() => setCount(count - 1)>減らす</button>
      <button onClick={() => setCount(0)>リセット</button>
    </div>
  );
}

複数のStateの管理

function Form() {
  const [name, setName] = useState('');
  const [email, setEmail] = useState('');
  const [age, setAge] = useState(20);

  return (
    <form>
      <input
        type="text"
        value={name}
        onChange={(e) => setName(e.target.value)}
        placeholder="名前"
      />
      <input
        type="email"
        value={email}
        onChange={(e) => setEmail(e.target.value)}
        placeholder="メールアドレス"
      />
      <input
        type="number"
        value={age}
        onChange={(e) => setAge(Number(e.target.value))}
        placeholder="年齢"
      />
    </form>
  );
}

クラス型コンポーネントでのState

class Counter extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    };
  }

  increment = () => {
    this.setState({ count: this.state.count + 1 });
  };

  decrement = () => {
    this.setState({ count: this.state.count - 1 });
  };

  reset = () => {
    this.setState({ count: 0 });
  };

  render() {
    return (
      <div>
        <p>カウント: {this.state.count}</p>
        <button onClick={this.increment}>増やす</button>
        <button onClick={this.decrement}>減らす</button>
        <button onClick={this.reset}>リセット</button>
      </div>
    );
  }
}

State更新の注意点

関数型コンポーネントでの注意

  1. 直接変更しない
   // 誤り
   count = count + 1;
   // 正しい
   setCount(count + 1);
  1. 前の状態に依存する更新
   setCount(prevCount => prevCount + 1);

クラス型コンポーネントでの注意

  1. 直接stateを変更しない
   // 誤り(再レンダリングが発生しない)
   this.state.count = this.state.count + 1;
   // 正しい
   this.setState({ count: this.state.count + 1 });
  1. state更新は非同期
   // 誤り(this.state.countが最新でない可能性)
   this.setState({ count: this.state.count + 1 });
   // 正しい
   this.setState((prevState) => ({
     count: prevState.count + 1
   }));

オブジェクト型のState

function UserForm() {
  const [user, setUser] = useState({
    name: '',
    email: '',
    age: 20
  });

  const handleChange = (e) => {
    const { name, value } = e.target;
    setUser(prevUser => ({
      ...prevUser, // 既存のstateを展開
      [name]: value // 変更されたプロパティを更新
    }));
  };

  return (
    <form>
      <input
        name="name"
        value={user.name}
        onChange={handleChange}
        placeholder="名前"
      />
      <input
        name="email"
        value={user.email}
        onChange={handleChange}
        placeholder="メールアドレス"
      />
      <input
        name="age"
        type="number"
        value={user.age}
        onChange={handleChange}
        placeholder="年齢"
      />
    </form>
  );
}

PropsとStateの使い分け

いつPropsを使うか?

  • 親から子コンポーネントにデータを渡すとき
  • コンポーネントの設定や表示内容を外部から制御したいとき
  • 複数のコンポーネントで同じデータを共有するとき

いつStateを使うか?

  • コンポーネント内部で状態を管理する必要があるとき
  • ユーザー操作に応じてUIを変化させたいとき
  • フォーム入力などのインタラクティブな要素を実装するとき

データフローの原則

Reactのデータフローは「単方向(unidirectional)」です:

  1. 親コンポーネントはPropsを使って子コンポーネントにデータを渡す
  2. 子コンポーネントはイベントを発行して親に変更を通知
  3. 親コンポーネントはStateを更新し、新しいPropsを子に渡す

実践的な使用例

親子コンポーネント間のデータフロー

// 親コンポーネント
function ParentComponent() {
  const [todos, setTodos] = useState([
    { id: 1, text: 'Reactを学ぶ', completed: false },
    { id: 2, text: 'プロジェクトを作成', completed: false },
    { id: 3, text: 'デプロイする', completed: false }
  ]);

  const toggleTodo = (id) => {
    setTodos(todos.map(todo =>
      todo.id === id ? { ...todo, completed: !todo.completed } : todo
    ));
  };

  return (
    <div>
      <h1>Todoリスト</h1>
      <TodoList todos={todos} onToggleTodo={toggleTodo} />
    </div>
  );
}

// 子コンポーネント
function TodoList({ todos, onToggleTodo }) {
  return (
    <ul>
      {todos.map(todo => (
        <li key={todo.id}>
          <input
            type="checkbox"
            checked={todo.completed}
            onChange={() => onToggleTodo(todo.id)}
          />
          <span style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}>
            {todo.text}
          </span>
        </li>
      ))}
    </ul>
  );
}

複数レベルのコンポーネント間でのProps渡し

function App() {
  const [user, setUser] = useState({
    name: '山田太郎',
    preferences: {
      theme: 'dark',
      fontSize: 16
    }
  });

  return (
    <div className={`app ${user.preferences.theme}`}>
      <Header user={user} />
      <MainContent user={user} />
      <Footer />
    </div>
  );
}

function Header({ user }) {
  return (
    <header>
      <h1>ようこそ、{user.name}さん</h1>
      <UserSettings user={user} />
    </header>
  );
}

function UserSettings({ user }) {
  return (
    <div className="settings">
      <p>現在のテーマ: {user.preferences.theme}</p>
      <p>フォントサイズ: {user.preferences.fontSize}px</p>
    </div>
  );
}

よくある間違いとベストプラクティス

Propsに関する注意点

  1. Propsの直接変更
   // 絶対にしないでください
   function ChildComponent(props) {
     props.name = '変更'; // エラーが発生
     // ...
   }
  1. 不必要なPropsの渡しすぎ
  • 深い階層に多くのPropsを渡すのは「Propsのバケツリレー」と呼ばれるアンチパターン
  • 解決策:Context APIや状態管理ライブラリの使用を検討

Stateに関する注意点

  1. Stateの過剰使用
  • 他のコンポーネントと共有しないデータだけをStateに
  • 導出可能なデータはStateに保持しない
  1. 深くネストしたState
   // 避けるべき
   const [user, setUser] = useState({
     profile: {
       personal: {
         name: '',
         address: {
           city: '',
           street: ''
         }
       }
     }
   });

   // 平坦化を検討
   const [name, setName] = useState('');
   const [city, setCity] = useState('');
   const [street, setStreet] = useState('');

パフォーマンス最適化

  1. Stateの分割
  • 関連のないデータは別々のStateに
  • 変更頻度の異なるデータも分離
  1. 不必要な再レンダリングの防止
  • 親コンポーネントのState変更が全ての子を再レンダリングする可能性
  • React.memoやuseMemoで最適化可能

デバッグ技法

Propsのデバッグ

function ChildComponent(props) {
  console.log('Received props:', props); // コンソールで確認
  return <div>...</div>;
}

Stateのデバッグ

function MyComponent() {
  const [state, setState] = useState(/* ... */);

  useEffect(() => {
    console.log('State changed:', state);
  }, [state]); // stateが変更されるたびにログ出力

  // ...
}

React Developer Toolsの使用

  • Componentsタブでコンポーネントツリーを調査
  • PropsとStateの現在値を確認
  • コンポーネントの再レンダリングをハイライト表示

まとめ:PropsとStateの重要なポイント

  1. Props
  • 親から子へデータを渡すための読み取り専用プロパティ
  • コンポーネントの設定や表示内容を制御
  • 単方向データフローの基盤
  1. State
  • コンポーネント内部で管理される変更可能なデータ
  • ユーザー操作に応じてUIを動的に更新
  • 変更時にコンポーネントが再レンダリング
  1. 使い分けの原則
  • データが親から子に流れる → Props
  • コンポーネント内部で管理される状態 → State
  1. 不変性の維持
  • Propsは変更しない
  • Stateは適切な方法でのみ更新

PropsとStateを適切に使い分けることで、Reactアプリケーションのデータフローを明確に保つことができます。次に学ぶイベント処理やライフサイクルメソッドと組み合わせることで、さらにインタラクティブなコンポーネントを作成できるようになります。