はじめに
Reactでアプリケーションを開発する際、最も重要な概念の一つが「コンポーネントの合成(Composition)」です。本記事では、小さなコンポーネントを組み合わせて、より複雑な機能を実現する方法について解説します。
コンポーネント合成とは
Reactは「継承」よりも「合成」を推奨しています。これは、小さくシンプルなコンポーネントを組み合わせることで、大きく複雑なアプリケーションを構築するアプローチです。
合成パターンの基本構造
概念 | 説明 |
---|---|
合成(Composition) | 小さなコンポーネントを組み合わせて大きなコンポーネントを作る |
継承(Inheritance) | Reactでは推奨されない。親クラスを拡張する手法 |
Props | 親から子へデータを渡す仕組み |
コールバック | 子から親へ情報を伝える関数 |
実装例:多肢選択コンポーネント
ラジオボタンを使った多肢選択問題コンポーネントを例に、合成パターンを実装してみます。
コンポーネントの階層構造
MultipleChoiceQuestion(親)
└── RadioInput(子)× 複数
└── input要素
1. RadioInputコンポーネントの実装
interface RadioInputProps {
id?: string;
name: string;
label: string;
value: string;
checked?: boolean;
onChanged?: (value: string) => void;
}
const RadioInput: React.FC<RadioInputProps> = ({
name,
label,
value,
checked = false,
onChanged
}) => {
const handleChange = () => {
onChanged?.(value);
};
return (
<div className="radio">
<label>
<input
type="radio"
name={name}
value={value}
checked={checked}
onChange={handleChange}
/>
{label}
</label>
</div>
);
};
2. MultipleChoiceQuestionコンポーネントの実装
interface Choice {
value: string;
label: string;
}
interface MultipleChoiceQuestionProps {
label: string;
choices: Choice[];
value?: string;
onCompleted: (value: string) => void;
}
const MultipleChoiceQuestion: React.FC<MultipleChoiceQuestionProps> = ({
label,
choices,
value,
onCompleted
}) => {
const [selectedValue, setSelectedValue] = useState(value);
const handleChanged = (newValue: string) => {
setSelectedValue(newValue);
onCompleted(newValue);
};
return (
<div className="form-group">
<label className="survey-item-label">{label}</label>
<div className="survey-item-content">
{choices.map((choice) => (
<RadioInput
key={choice.value}
name="question"
label={choice.label}
value={choice.value}
checked={selectedValue === choice.value}
onChanged={handleChanged}
/>
))}
</div>
</div>
);
};
親子間の通信パターン
データフローの仕組み
方向 | 手段 | 用途 |
---|---|---|
親→子 | Props | データや設定値の受け渡し |
子→親 | コールバック関数 | イベントや状態変更の通知 |
通信の実装ポイント
- 親コンポーネント:コールバック関数を定義してpropsとして渡す
- 子コンポーネント:イベント発生時にコールバック関数を呼び出す
- 状態管理:親コンポーネントで一元管理することが推奨
使用例
const App: React.FC = () => {
const choices = [
{ value: "1", label: "選択肢A" },
{ value: "2", label: "選択肢B" },
{ value: "3", label: "選択肢C" }
];
const handleComplete = (value: string) => {
console.log("選択された値:", value);
};
return (
<MultipleChoiceQuestion
label="質問内容"
choices={choices}
onCompleted={handleComplete}
/>
);
};
まとめ
コンポーネントの合成パターンを使用することで、以下のメリットが得られます。
- 再利用性の向上:小さなコンポーネントは様々な場面で再利用可能
- 保守性の向上:各コンポーネントの責務が明確になる
- テストの容易さ:独立したコンポーネントは個別にテスト可能
- 柔軟な拡張:新機能の追加が容易
Reactの合成パターンは、HTMLの要素を拡張する自然な方法です。基本的な要素から始めて、段階的に機能を追加していくことで、複雑なUIコンポーネントを構築できます。このアプローチにより、コードの見通しが良くなり、メンテナンスしやすいアプリケーションを開発することができます。