Reactのコンポーネント設計で失敗しない!シングルレスポンシビリティ原則の適用法

Reactアプリ開発において、コンポーネント設計はアプリの成功を左右する重要な要素です。特に、シングルレスポンシビリティ原則(Single Responsibility Principle: SRP)を適用することは、コードの可読性や保守性を向上させ、バグのリスクを低減するために不可欠です。本記事では、Reactコンポーネントにおける責務分担の重要性を理解し、効率的な設計を行うための具体的な方法を解説します。これにより、堅牢でスケーラブルなアプリケーションを構築するための知識を習得できます。

目次

シングルレスポンシビリティ原則とは


シングルレスポンシビリティ原則(Single Responsibility Principle: SRP)は、ソフトウェア開発における設計原則の一つで、「1つのモジュール(またはクラス、関数)は、1つの明確な目的または責務を持つべきである」という考え方です。

Reactコンポーネントへの適用


Reactにおいて、この原則を適用することで、以下のような利点が得られます。

1. 可読性の向上


各コンポーネントが単一の目的を持つため、コードを読む開発者が簡単に役割を理解できます。

2. 保守性の向上


責務が明確であるため、変更の影響範囲を予測しやすく、修正が容易です。

3. 再利用性の向上


単一の機能に特化したコンポーネントは、他の部分でも容易に再利用できます。

具体例


例えば、以下のようなフォームの例を考えます。

// 過剰な責務を持つコンポーネント
const RegistrationForm = () => {
  const [username, setUsername] = useState('');
  const [password, setPassword] = useState('');
  const handleSubmit = () => {
    // バリデーション
    // サーバー送信
  };

  return (
    <form onSubmit={handleSubmit}>
      <input value={username} onChange={(e) => setUsername(e.target.value)} />
      <input type="password" value={password} onChange={(e) => setPassword(e.target.value)} />
      <button type="submit">Register</button>
    </form>
  );
};

このコンポーネントでは、入力の管理、バリデーション、サーバー送信の責務がすべて含まれています。

SRPを適用すると以下のように分割できます。

const InputField = ({ value, onChange, type = 'text' }) => (
  <input type={type} value={value} onChange={onChange} />
);

const RegistrationForm = () => {
  const [username, setUsername] = useState('');
  const [password, setPassword] = useState('');

  const handleSubmit = () => {
    // サーバー送信
  };

  return (
    <form onSubmit={handleSubmit}>
      <InputField value={username} onChange={(e) => setUsername(e.target.value)} />
      <InputField type="password" value={password} onChange={(e) => setPassword(e.target.value)} />
      <button type="submit">Register</button>
    </form>
  );
};

このように分割することで、コードの可読性や再利用性が大幅に向上します。

過剰な責務の問題点

Reactコンポーネントに過剰な責務を持たせると、さまざまな問題が発生します。ここでは、その問題点と具体例を挙げて解説します。

問題点1: コードの可読性の低下


1つのコンポーネントが多くの責務を担うと、コードが肥大化し、他の開発者が役割を理解しにくくなります。以下の例をご覧ください。

const Dashboard = () => {
  const [userData, setUserData] = useState(null);
  const [notifications, setNotifications] = useState([]);
  const [theme, setTheme] = useState('light');

  useEffect(() => {
    // ユーザーデータの取得
  }, []);

  useEffect(() => {
    // 通知データの取得
  }, []);

  return (
    <div>
      <h1>Dashboard</h1>
      <UserProfile data={userData} />
      <NotificationsList data={notifications} />
      <ThemeSwitcher theme={theme} onChange={setTheme} />
    </div>
  );
};

このコンポーネントは、ユーザーデータの取得、通知の管理、テーマの切り替えといった多くの責務を持っており、メンテナンスが難しくなります。

問題点2: テストの困難さ


責務が集中しているコンポーネントは、その挙動を検証するために多くのユースケースをテストする必要があります。例えば、以下のような問題が発生します。

  • データ取得のテストとUIの描画テストが混在する
  • 状態管理とイベント処理の相互依存性が高まり、個別にテストしにくい

問題点3: 再利用性の欠如


複数の責務を抱えるコンポーネントは、特定の用途に特化しているため、他の場面で再利用しにくくなります。

実例: 過剰な責務を持つボタン

以下のようなボタンコンポーネントを考えます。

const SubmitButton = ({ onClick }) => {
  const handleClick = () => {
    // バリデーション処理
    // サーバー通信処理
    onClick();
  };

  return <button onClick={handleClick}>Submit</button>;
};

このボタンは、UIの描画以外にバリデーションやサーバー通信といった責務を持っています。

解決策への導入


過剰な責務を解消するためには、シングルレスポンシビリティ原則を適用し、以下のように役割を分離します。

const SubmitButton = ({ onClick }) => <button onClick={onClick}>Submit</button>;

const validateAndSubmit = () => {
  // バリデーション処理
  // サーバー通信処理
};

<SubmitButton onClick={validateAndSubmit} />;

このように、UIコンポーネントとビジネスロジックを分離することで、再利用性が高まり、コードがシンプルになります。

責務を明確化する手法

Reactコンポーネントにおける責務を明確にすることは、設計をシンプルに保ち、コードの保守性を向上させるために重要です。ここでは、責務を整理するための具体的な手法を解説します。

1. コンポーネントの種類を明確にする


Reactコンポーネントは主に以下の2種類に分類できます。

プレゼンテーションコンポーネント


UIの描画に特化し、ロジックや状態を持たないコンポーネントです。例: ボタン、モーダル、カード。

const Card = ({ title, content }) => (
  <div className="card">
    <h3>{title}</h3>
    <p>{content}</p>
  </div>
);

コンテナコンポーネント


状態管理やロジックを担当し、プレゼンテーションコンポーネントを組み合わせて使用します。

const CardContainer = () => {
  const [data, setData] = useState({ title: "Hello", content: "World" });

  useEffect(() => {
    // データ取得ロジック
  }, []);

  return <Card title={data.title} content={data.content} />;
};

2. 単一責務の定義


コンポーネントごとに、「このコンポーネントは何をするためのものか」を明確に定義します。

  • プレゼンテーション専用: UI描画のみ
  • ロジック専用: 状態管理やデータ処理

3. プロパティを利用した依存の削減


状態やロジックをコンテナ側に持たせ、プレゼンテーションコンポーネントには必要なデータだけをプロパティとして渡します。

const UserProfile = ({ name, age }) => (
  <div>
    <h1>{name}</h1>
    <p>Age: {age}</p>
  </div>
);

const UserContainer = () => {
  const [user, setUser] = useState({ name: "Alice", age: 25 });

  return <UserProfile name={user.name} age={user.age} />;
};

4. カスタムフックを活用する


ロジックをカスタムフックに分離し、UIコードとロジックコードをさらに明確に分割します。

const useUserData = () => {
  const [user, setUser] = useState(null);

  useEffect(() => {
    // データ取得ロジック
    setUser({ name: "Alice", age: 25 });
  }, []);

  return user;
};

const UserContainer = () => {
  const user = useUserData();

  if (!user) return <p>Loading...</p>;
  return <UserProfile name={user.name} age={user.age} />;
};

5. 高度な分割のための設計ツール


デザインツールや状態管理ツール(Redux, Zustandなど)を用いると、責務の整理がさらに効率化します。これにより、UI、状態管理、ビジネスロジックを完全に分離できます。

実践例


以下のような手順で責務を分けていくとスムーズです。

  1. コンポーネントの目的を整理
  2. プレゼンテーションとロジックの分離
  3. カスタムフックを適用
  4. 再利用性とスケーラビリティを検証

これらの手法を適用することで、Reactプロジェクトの責務を明確化し、コード品質を向上させることができます。

コンポーネント分割の実例

コンポーネント分割は、Reactアプリケーションの設計において重要なステップです。本セクションでは、具体的なコード例を用いて、適切なコンポーネント分割の方法を解説します。

例題: タスク管理アプリ


以下のようなタスク管理アプリを考えます。機能として、タスクの追加、一覧表示、完了状態の切り替えを含みます。

未分割のコード例

const TaskApp = () => {
  const [tasks, setTasks] = useState([]);
  const [newTask, setNewTask] = useState("");

  const addTask = () => {
    setTasks([...tasks, { id: Date.now(), text: newTask, completed: false }]);
    setNewTask("");
  };

  const toggleTaskCompletion = (taskId) => {
    setTasks(tasks.map(task =>
      task.id === taskId ? { ...task, completed: !task.completed } : task
    ));
  };

  return (
    <div>
      <h1>Task Manager</h1>
      <input
        type="text"
        value={newTask}
        onChange={(e) => setNewTask(e.target.value)}
      />
      <button onClick={addTask}>Add Task</button>
      <ul>
        {tasks.map(task => (
          <li key={task.id}>
            <span
              style={{ textDecoration: task.completed ? "line-through" : "none" }}
              onClick={() => toggleTaskCompletion(task.id)}
            >
              {task.text}
            </span>
          </li>
        ))}
      </ul>
    </div>
  );
};

このコードは、状態管理、ロジック、UIのすべてが1つのコンポーネントに詰め込まれており、保守性が低い状態です。

分割後のコード例

以下のように、責務ごとにコンポーネントを分割します。

1. プレゼンテーションコンポーネント:TaskList
タスク一覧を表示するだけのコンポーネントに分割します。

const TaskList = ({ tasks, onToggle }) => (
  <ul>
    {tasks.map(task => (
      <li key={task.id}>
        <span
          style={{ textDecoration: task.completed ? "line-through" : "none" }}
          onClick={() => onToggle(task.id)}
        >
          {task.text}
        </span>
      </li>
    ))}
  </ul>
);

2. プレゼンテーションコンポーネント:TaskInput
タスクを入力するUI部分を独立させます。

const TaskInput = ({ value, onChange, onAdd }) => (
  <div>
    <input
      type="text"
      value={value}
      onChange={(e) => onChange(e.target.value)}
    />
    <button onClick={onAdd}>Add Task</button>
  </div>
);

3. コンテナコンポーネント:TaskApp
状態管理やロジックを担うメインコンポーネントとします。

const TaskApp = () => {
  const [tasks, setTasks] = useState([]);
  const [newTask, setNewTask] = useState("");

  const addTask = () => {
    setTasks([...tasks, { id: Date.now(), text: newTask, completed: false }]);
    setNewTask("");
  };

  const toggleTaskCompletion = (taskId) => {
    setTasks(tasks.map(task =>
      task.id === taskId ? { ...task, completed: !task.completed } : task
    ));
  };

  return (
    <div>
      <h1>Task Manager</h1>
      <TaskInput
        value={newTask}
        onChange={setNewTask}
        onAdd={addTask}
      />
      <TaskList
        tasks={tasks}
        onToggle={toggleTaskCompletion}
      />
    </div>
  );
};

分割による利点

1. 再利用性の向上


TaskInputやTaskListは他のプロジェクトやコンテナコンポーネントで簡単に再利用できます。

2. 保守性の向上


ロジックがTaskAppに集中し、UIはプレゼンテーションコンポーネントに分散することで、それぞれが独立して編集可能になります。

3. テストの容易さ


TaskInputやTaskListを個別にテストすることで、ロジックやUIのバグを特定しやすくなります。

このような分割を行うことで、Reactアプリケーションの設計がスケーラブルでメンテナンスしやすくなります。

状態管理と責務分担の関係

Reactコンポーネント設計において、状態管理は責務分担を明確化する上で重要な要素です。適切に状態を管理することで、コンポーネントの役割を明確にし、コードの保守性や拡張性を向上させることができます。

状態管理の基本


状態管理とは、コンポーネント内のデータ(状態)を制御する仕組みを指します。Reactでは、以下の方法で状態を管理します。

1. useState


シンプルな状態管理には、useStateフックを使用します。

const Counter = () => {
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
};

2. useReducer


複雑なロジックを持つ状態管理には、useReducerフックが適しています。

const reducer = (state, action) => {
  switch (action.type) {
    case "increment":
      return { count: state.count + 1 };
    case "decrement":
      return { count: state.count - 1 };
    default:
      return state;
  }
};

const Counter = () => {
  const [state, dispatch] = useReducer(reducer, { count: 0 });

  return (
    <div>
      <p>Count: {state.count}</p>
      <button onClick={() => dispatch({ type: "increment" })}>Increment</button>
      <button onClick={() => dispatch({ type: "decrement" })}>Decrement</button>
    </div>
  );
};

3. グローバル状態管理ツール


アプリ全体で共有される状態を管理する場合、以下のツールが便利です。

  • Redux
  • Zustand
  • Context API(React組み込みの機能)

状態管理と責務分担の実践例


例として、ToDoアプリの状態管理と責務分担を考えます。

1. 過剰な責務を持つコンポーネント


状態管理とUIが混在している場合、以下のようになります。

const TodoApp = () => {
  const [tasks, setTasks] = useState([]);

  const addTask = (task) => {
    setTasks([...tasks, task]);
  };

  return (
    <div>
      <h1>Todo List</h1>
      <input
        type="text"
        onKeyPress={(e) => {
          if (e.key === "Enter") {
            addTask(e.target.value);
            e.target.value = "";
          }
        }}
      />
      <ul>
        {tasks.map((task, index) => (
          <li key={index}>{task}</li>
        ))}
      </ul>
    </div>
  );
};

このコードでは、状態管理とUIが混在しており、責務が不明確です。

2. 状態管理を分離したコンポーネント


責務を明確にするため、状態管理をカスタムフックに分離します。

const useTodoState = () => {
  const [tasks, setTasks] = useState([]);

  const addTask = (task) => {
    setTasks([...tasks, task]);
  };

  return { tasks, addTask };
};

const TodoApp = () => {
  const { tasks, addTask } = useTodoState();

  return (
    <div>
      <h1>Todo List</h1>
      <TodoInput onAdd={addTask} />
      <TodoList tasks={tasks} />
    </div>
  );
};

const TodoInput = ({ onAdd }) => {
  const [input, setInput] = useState("");

  const handleKeyPress = (e) => {
    if (e.key === "Enter") {
      onAdd(input);
      setInput("");
    }
  };

  return (
    <input
      type="text"
      value={input}
      onChange={(e) => setInput(e.target.value)}
      onKeyPress={handleKeyPress}
    />
  );
};

const TodoList = ({ tasks }) => (
  <ul>
    {tasks.map((task, index) => (
      <li key={index}>{task}</li>
    ))}
  </ul>
);

分離による利点

1. 再利用性


useTodoStateTodoInputは他のアプリでも再利用可能です。

2. 責務の明確化


TodoAppは状態管理を提供し、UIの構成に専念できます。

3. テストの容易さ


状態管理ロジックとUIロジックを独立してテスト可能になります。

まとめ


状態管理を責務ごとに分けることで、コンポーネント設計がシンプルになり、Reactアプリのスケーラビリティと保守性が向上します。

再利用性を高める設計

Reactコンポーネントの再利用性を高めることは、効率的な開発とメンテナンスにおいて非常に重要です。本セクションでは、再利用可能なコンポーネントを設計するための具体的な手法を解説します。

1. プロパティ駆動型の設計


コンポーネントに柔軟性を持たせるために、プロパティ(props)を使って動的に振る舞いを変更できるようにします。

具体例: 汎用ボタンコンポーネント

const Button = ({ label, onClick, type = "button", style = {} }) => (
  <button type={type} onClick={onClick} style={style}>
    {label}
  </button>
);

// 再利用例
<Button label="Submit" onClick={handleSubmit} style={{ backgroundColor: "blue" }} />
<Button label="Cancel" onClick={handleCancel} style={{ backgroundColor: "red" }} />

Buttonコンポーネントは、プロパティを使うことで、異なる用途に再利用できます。

2. コンポジションを活用する


Reactの子コンポーネント(children)を利用して、汎用的なコンポーネントを作成します。

具体例: 汎用カードコンポーネント

const Card = ({ children, style = {} }) => (
  <div className="card" style={style}>
    {children}
  </div>
);

// 再利用例
<Card style={{ padding: "20px", border: "1px solid gray" }}>
  <h2>Title</h2>
  <p>Content goes here</p>
</Card>
<Card>
  <img src="example.jpg" alt="Example" />
  <p>Description of the image</p>
</Card>

このように、Cardコンポーネントはさまざまな内容を柔軟に表示できます。

3. カスタムフックを導入する


再利用可能なロジックをカスタムフックとして抽出し、複数のコンポーネントで利用します。

具体例: フォーム入力の管理フック

const useForm = (initialValues) => {
  const [values, setValues] = useState(initialValues);

  const handleChange = (e) => {
    const { name, value } = e.target;
    setValues({ ...values, [name]: value });
  };

  return [values, handleChange];
};

// 再利用例
const LoginForm = () => {
  const [formValues, handleChange] = useForm({ username: "", password: "" });

  return (
    <form>
      <input
        name="username"
        value={formValues.username}
        onChange={handleChange}
        placeholder="Username"
      />
      <input
        name="password"
        type="password"
        value={formValues.password}
        onChange={handleChange}
        placeholder="Password"
      />
      <button type="submit">Login</button>
    </form>
  );
};

このカスタムフックは、フォーム入力の状態管理を抽象化し、さまざまなフォームで再利用可能です。

4. コンポーネントの分割とモジュール化


各コンポーネントを1つの目的に集中させることで、再利用性を高めます。

具体例: タスク管理アプリの分割

const TaskList = ({ tasks, onToggle }) => (
  <ul>
    {tasks.map((task) => (
      <li key={task.id}>
        <span
          onClick={() => onToggle(task.id)}
          style={{ textDecoration: task.completed ? "line-through" : "none" }}
        >
          {task.text}
        </span>
      </li>
    ))}
  </ul>
);

const TaskInput = ({ value, onChange, onAdd }) => (
  <div>
    <input value={value} onChange={(e) => onChange(e.target.value)} />
    <button onClick={onAdd}>Add Task</button>
  </div>
);

これらのコンポーネントを組み合わせて、さまざまなタスク管理アプリを構築できます。

5. スタイルとロジックの分離


スタイルとロジックを分離することで、UIコンポーネントを再利用しやすくします。CSSモジュールやスタイルライブラリ(例えばTailwind CSS)を活用するのも効果的です。

まとめ


再利用性を高めるには、プロパティ駆動型設計、コンポジション、カスタムフック、モジュール化、スタイルとロジックの分離といった手法を活用することが重要です。これにより、開発効率が向上し、長期的な保守性も確保できます。

チーム開発におけるメリット

Reactコンポーネント設計にシングルレスポンシビリティ原則(SRP)を適用することで、チーム開発に多くのメリットをもたらします。本セクションでは、これらのメリットを具体的に解説します。

1. コードの可読性と理解の向上


SRPを適用したコンポーネントは、各コンポーネントが1つの目的を持つため、コードの可読性が向上します。

  • 明確な役割分担: 開発者がそれぞれのコンポーネントの役割をすぐに理解できる。
  • 迅速なレビュー: 他のメンバーがコードを素早くレビュー可能。

例: フォームコンポーネント

// 責務が明確化されている場合
const LoginForm = () => {
  return (
    <div>
      <InputField label="Username" />
      <InputField label="Password" type="password" />
      <SubmitButton onClick={handleSubmit} />
    </div>
  );
};

チーム全員がコンポーネントの役割をすぐに把握できるため、メンテナンスや開発がスムーズです。

2. コンポーネントの再利用による効率化


責務が明確で再利用可能なコンポーネントを作成することで、チームメンバーが共通のパーツを使い回すことができます。

  • 重複作業の削減: 似たような機能のコードを何度も書く必要がなくなる。
  • 一貫性の向上: アプリケーション全体でUIや動作が統一される。

例: ボタンコンポーネントの再利用

const PrimaryButton = ({ label, onClick }) => (
  <button className="primary" onClick={onClick}>
    {label}
  </button>
);

// 様々な場面で再利用
<PrimaryButton label="Save" onClick={saveData} />
<PrimaryButton label="Delete" onClick={deleteData} />

3. 並行作業の促進


コンポーネントが小さく独立していることで、複数のメンバーが同時に異なる部分を担当できます。

  • 衝突の回避: 作業範囲が明確で、コードの競合を減らせる。
  • 効率的な分担: 特定のコンポーネントや機能に専念できる。

実例: ページの分担

  • Aさん: ヘッダーコンポーネントの作成
  • Bさん: フッターコンポーネントの作成
  • Cさん: メインコンテンツのデータロジック

これにより、作業が並列化され、プロジェクトの進行が迅速になります。

4. バグの特定と修正が容易に


責務が明確化されているため、どのコンポーネントに問題があるか特定しやすくなります。

  • 影響範囲の限定: 修正が必要な箇所が明確で、他の機能に影響を与えにくい。
  • トラブルシューティングの迅速化: 問題が発生した場合でも該当コンポーネントに集中して修正可能。

例: バグの特定


フォームバリデーションの不具合が発生した場合、責務が分かれていれば以下のように迅速に対応できます。

  • バリデーションロジックがuseValidationフックにあることを特定。
  • 他のUI部分への影響を心配せずに修正可能。

5. ドキュメント作成の容易さ


責務が明確なコンポーネントは、ドキュメントもシンプルでわかりやすくなります。

  • 各コンポーネントの説明が簡潔: コンポーネント名と役割を記載するだけで十分。
  • デモや使用例の提供が容易: 各コンポーネントに対応した使用例をすぐに用意可能。

6. 新メンバーのオンボーディングを簡略化


新しいチームメンバーがプロジェクトに参加した場合でも、責務が明確な設計によってプロジェクト構造を迅速に理解できます。

  • 学習コストの低下: 分割されたコンポーネント単位で学習可能。
  • 初期の生産性向上: 小さな責務を持つコンポーネントから作業を始められる。

まとめ


ReactでSRPを導入した設計は、チーム開発におけるコードの可読性向上、効率化、バグ修正の迅速化など、多くの利点をもたらします。結果として、プロジェクト全体の生産性と品質が向上します。

応用例と演習問題

Reactコンポーネントにシングルレスポンシビリティ原則(SRP)を適用することで、どのように現実のアプリケーションに応用できるのかを具体例で示します。また、学んだ内容を確認するための演習問題も提供します。

応用例: ショッピングカートアプリ

ショッピングカートアプリでは、以下のような機能が必要です。

  1. 商品リストの表示
  2. カートへの追加・削除
  3. 合計金額の表示

SRPを適用してこれらの責務を分割したコンポーネント設計を行います。

1. 商品リストコンポーネント

const ProductList = ({ products, onAddToCart }) => (
  <div>
    {products.map((product) => (
      <div key={product.id}>
        <h3>{product.name}</h3>
        <p>{product.price} USD</p>
        <button onClick={() => onAddToCart(product)}>Add to Cart</button>
      </div>
    ))}
  </div>
);

責務: 商品データを受け取り、リスト形式で表示。カートへの追加イベントをトリガー。

2. カートコンポーネント

const Cart = ({ cartItems, onRemoveFromCart }) => (
  <div>
    <h2>Your Cart</h2>
    {cartItems.map((item) => (
      <div key={item.id}>
        <h3>{item.name}</h3>
        <p>{item.price} USD</p>
        <button onClick={() => onRemoveFromCart(item.id)}>Remove</button>
      </div>
    ))}
  </div>
);

責務: カート内の商品を表示し、削除機能を提供。

3. 合計金額表示コンポーネント

const CartSummary = ({ total }) => (
  <div>
    <h3>Total: {total} USD</h3>
  </div>
);

責務: カートの合計金額を計算し表示。

4. コンテナコンポーネント

const ShoppingCartApp = () => {
  const [products] = useState([
    { id: 1, name: "Apple", price: 1 },
    { id: 2, name: "Banana", price: 2 },
    { id: 3, name: "Cherry", price: 3 },
  ]);
  const [cart, setCart] = useState([]);

  const addToCart = (product) => {
    setCart([...cart, product]);
  };

  const removeFromCart = (productId) => {
    setCart(cart.filter((item) => item.id !== productId));
  };

  const total = cart.reduce((sum, item) => sum + item.price, 0);

  return (
    <div>
      <ProductList products={products} onAddToCart={addToCart} />
      <Cart cartItems={cart} onRemoveFromCart={removeFromCart} />
      <CartSummary total={total} />
    </div>
  );
};

責務: 状態管理(商品リスト、カートの状態)とイベント処理を統括。

演習問題

以下の演習問題に取り組むことで、SRPを実践的に理解できます。

問題1: タスクフィルタリング


ToDoアプリにフィルタ機能を追加してください。

  • 機能: 全タスク、完了済みタスク、未完了タスクを切り替えられるフィルタボタンを作成する。
  • 責務分担:
  1. フィルタ状態の管理を行うコンテナコンポーネントを作成。
  2. フィルタボタンとタスクリストを分離。

問題2: フォームのバリデーション


ログインフォームに以下のバリデーション機能を追加してください。

  • 要件:
  1. ユーザー名は空白不可。
  2. パスワードは8文字以上必要。
  • 責務分担:
  1. バリデーションロジックをカスタムフックで抽出。
  2. 入力コンポーネントにエラーメッセージを表示。

問題3: カスタムフックの作成


データ取得のロジックをカスタムフックとして分離してください。

  • 機能: REST APIを使用して商品データを取得。
  • 責務分担:
  1. useFetchProductsカスタムフックを作成。
  2. 商品データを受け取って表示するコンポーネントを作成。

まとめ


これらの応用例と演習問題を通じて、SRPを実践的に活用するスキルを磨くことができます。責務分担を意識した設計を取り入れれば、Reactアプリの品質と保守性が飛躍的に向上します。

まとめ

本記事では、Reactコンポーネント設計におけるシングルレスポンシビリティ原則(SRP)の重要性を解説しました。SRPを適用することで、コンポーネントの役割が明確になり、コードの可読性、再利用性、保守性が向上します。特に、チーム開発においては並行作業がしやすくなり、プロジェクト全体の生産性向上に貢献します。

応用例や演習問題を通じて、具体的な実装方法や課題へのアプローチも学びました。SRPを意識した設計を取り入れることで、よりスケーラブルで効率的なReactアプリケーションを構築できるようになるでしょう。

コメント

コメントする

目次