好きだからという理由だけでReactコンポーネントを追加するのはやめよう

好きだからという理由だけでReactコンポーネントを追加するのはやめよう

すべての新しい抽象化に疑問を投げかけ、より良いアーキテクチャ設計を行う方法

すべての始まりとなった見出し

現在、至る所のコードベースでこんな会話が交わされています。誰かが新しいReactコンポーネントを提案します。無害に見えます。しかし、別の誰かが「本当にこれが必要なのか?」と尋ねます。その答えはしばしば人々を驚かせます。

2026年7月14日にDEV Communityで公開された記事の中で、ある開発者が自身のコードベースにおけるアーキテクチャの議論について説明していました。数人の同僚が新しいHeadingコンポーネントの導入を望んでいました。コードは以下のようになります:

<Heading level={3}>Profile Settings</Heading>

クリーンです。セマンティックです。整理されているように感じます。しかし、問題がありました。

すでに3つの解決策がある

チームはすでに以下のように書くことができました:

<h3>Profile Settings</h3>

また、まさにこのような柔軟性のために構築された既存のTextコンポーネントを使用することもできました:

<Text as="h3">Profile Settings</Text>

では、なぜ4つ目の方法を追加するのでしょうか?尋ねられたときの答えは正直なものでした。「見出し用のコンポーネントが別にあるのが好きなんだ。」

それは人間としての理由です。悪い理由ではありません。しかし、エンジニアリングとしての理由ではありません。

個人の好みは設計原則ではない

フロントエンドチームは抽象化が大好きです。時々少し好きすぎることがあります。ラッパーがラップされます。それらをラップしたラッパーがさらにラップされます。元のHTMLは認識できなくなります。

その正当化はよく聞くものです。「よりクリーンに見える。」「より一貫性があるように感じる。」「私が好きだから。」

これらは理解できます。共感できます。しかし、エンジニアリングの正当化としては弱いものです。理由はこうです。すべての新しいコンポーネントには何らかのコストがかかります。

  • 書くべきドキュメント
  • 保守すべきコード
  • 最新に保つべきテスト
  • トレーニングすべき新しい開発者
  • 変更が生じたときの移行パス
  • チームを混乱させるAPIの重複

共有コードベースは誰かの個人的な砂場ではありません。すべての抽象化は、チームの長期的な負担の一部になります。その負担には、単なる好みよりも強力な正当化が必要です。

HTMLはすでに慎重に設計されたAPIである

私たちは時々これを忘れます。HTMLは原始的なものではありません。粗野なものでもありません。それは上に構築できる、思慮深くエンジニアリングされた抽象化ですが、常に置き換えられるものではありません。

見出しを例にとってみましょう。ネイティブの階層構造はすでに以下を提供しています:

  • セマンティックな意味 (h1はh3よりも重要です)
  • 組み込みのアクセシビリティサポート
  • スクリーンリーダーの互換性
  • SEOコンテキスト
  • ドキュメントのアウトライン構造

これらはどれも副作用ではありません。標準に組み込まれた設計なのです。

このようにカスタムコンポーネントでラップすると:

<Heading level={1}>Dashboard</Heading>
<Heading level={2}>Analytics</Heading>
<Heading level={3}>Revenue</Heading>

ほとんどの場合、出力は改善されません。構文を変更しているだけです。そして、振る舞いを変更せずに構文を変更することは、コストに見合う価値があることは稀です。

すべてを変える1つのルール

これらの決定を導くべき原則は以下の通りです: 構文を抽象化するな。振る舞いを抽象化せよ。

それだけです。その1つの区別が、不必要な議論のほとんどを終わらせます。

悪い抽象化は、ネイティブHTMLをあまりにも忠実に模倣します:

<Heading level={3}>Title</Heading>

なぜこれが悪いのでしょうか?なぜなら、主にh3を模倣しているからです。新しいものを何も追加していません。

良い抽象化は、実際の振る舞いを追加します:

<Text variant="muted" size="small" truncate>
  Description
</Text>

これにより以下が一元化されます:

  • デザイントークンのマッピング
  • テキストの切り詰めロジック
  • テーマの一貫性
  • レスポンシブな振る舞い

それが価値です。抽象化のコストに見合う価値があります。

1つの概念は1つのAPIを所有すべき

コードベースでは混乱がすぐに広がります。これは完璧な温床です:

<Heading level={3}>Profile</Heading>
<Text as="h3">Profile</Text>
<h3>Profile</h3>

同じことを書くための3つの方法。今や、すべてのプルリクエストがスタイルの議論になります。「Headingを使うべきか、Textか、ネイティブのh3か?」誰も本当のところは分かりません。

良いシステムは選択肢を減らします。有用な原則はこれです: 1つの概念に、1つのAPI。 HTMLがすでにセマンティクスを処理しているなら、セマンティクスを所有させましょう。Textコンポーネントがタイポグラフィを所有しているなら、タイポグラフィを所有させましょう。明確な分離。明確な所有権。

コンポジションは抽象化に勝る

機能のすべての組み合わせに対してラッパーを作成する代わりに、コンポジションを優先しましょう。

これは避けてください:

<Heading level={2}>Billing Settings</Heading>

こちらを優先してください:

<h2>
  <Text variant="secondary">
    Billing Settings
  </Text>
</h2>

以下が得られます:

  • ネイティブHTMLのセマンティクス (h2は依然としてh2の意味を持ちます)
  • 再利用可能なタイポグラフィトークン (Textバリアントがスタイリングを処理します)
  • 明確な責任 (h2は構造用、Textは外観用)
  • 重複のないAPI
  • 曖昧さの排除

コンポジションは、別の抽象化レイヤーを追加するよりも柔軟性があり、混乱を減らすことができます。

構築する前の5つの質問

新しいコンポーネントを提案する前に、自問してください:

1. これは実際にどのような問題を解決するのか? 「よりクリーンに見える」や「この方が好き」ではありません。「アクセシブルな階層を強制する」や「フォントサイズの不一致を防ぐ」のような測定可能なものです。

2. HTMLはすでにそれを解決していないか? もしそうなら:なぜ置き換えるのでしょうか?その負担には深刻な正当化が必要です。

3. 複雑さを軽減するか? それとも単に複雑さを移動させて、コンポーネント内に隠しているだけでしょうか?

4. APIの重複を導入していないか? 同じことをするための重複した方法は、一貫性のないコードベースを作り出します。それはコストであり、機能ではありません。

5. 振る舞いを追加しているか? もしコンポーネントが振る舞いを追加せずに構文を変更するだけなら、おそらくそれは存在すべきではありません。

結論

すべての繰り返されるパターンがコンポーネントに値するわけではありません。すべてのHTML要素がReactラッパーを必要とするわけではありません。そして、すべての好みが共有の抽象化になるべきわけでもありません。重要な質問はこれです:この抽象化は新しい能力を生み出しているのか、それとも単に新しい構文を生み出しているだけなのか?それに正直に答えれば、誰も実際には必要としていない何年もの複雑さからコードベースを守ることができるでしょう。

メリット

  • コンポーネントがいつ現実の問題を解決するかを判断するための測定可能な基準を提供します
  • ネイティブHTML要素のアクセシビリティとセマンティックな価値を保持します
  • 同じタスクを達成するための複数の方法によって引き起こされるチームの混乱を減らします
  • 単なる構文の変更ではなく、新しい振る舞いの追加にコンポーネント作成の焦点を当てます
  • ラッパーによる抽象化よりも柔軟なコンポジションを奨励します
  • コードベースが不必要なコンポーネントの層の下に埋もれてしまうのを防ぐのに役立ちます

デメリット

  • カスタムの抽象化を構築することを好む開発者は、このアプローチを制限的だと感じるかもしれません
  • すでに多くのカスタムコンポーネントを持っているコードベースは、大規模なリファクタリング作業に直面します
  • 何が「振る舞い」であり、何が「構文」としてカウントされるかを決定するには、継続的なチームの議論と合意が必要です
  • コンポジションパターンは、階層的なコンポーネント構造よりも整理されていないように感じることが時々あります

注意

この記事は、公開された情報源からのアーキテクチャの原則を説明しています。コード例は説明用であり、プレースホルダーのJSX構文を使用しています。これらの原則を独自のコードベースに適用する前に、元の情報源に照らして主張を検証し、チームの特定のコンテキストと既存のパターンに合わせてガイダンスを適応させてください。すべてのチームの状況は異なります。あるチームで機能するものが、別のチームでは調整が必要になる場合があります。ルールを一方的に適用するのではなく、アーキテクチャの決定については常にチームで話し合ってください。

よくある質問

HTMLを使用する代わりに、いつカスタムコンポーネントを作成すべきですか? — デザイントークン、レスポンシブなロジック、一元化されたスタイリングなどの振る舞いを追加する場合は、コンポーネントを作成してください。コンポーネントがロジックを追加せずに構文のみを変更する場合は、通常、ネイティブHTMLまたはコンポジションの方がうまく機能します。

良い抽象化と悪い抽象化の違いをどのように見分ければよいですか? — 自問してください:このコンポーネントは、HTMLがまだ解決していない問題を解決していますか?振る舞いやロジックを一元化していますか?「はい」の場合、それはおそらく良いものです。機能を追加せずに単にHTMLをラップしているだけなら、おそらく不必要です。

チームがすでに多すぎるカスタムコンポーネントを持っている場合はどうすればよいですか? — どのコンポーネントが実際の振る舞いを追加し、どのコンポーネントが単にHTML構文をラップしているのかを特定することから始めましょう。現実の問題を解決しているものは維持します。構文のみのラッパーについては、徐々にネイティブHTMLまたはコンポジションに移行します。

なぜコンポジションがラッパーコンポーネントよりも優れているのですか? — コンポジションは、ネイティブHTMLのセマンティクスを保持し、デザイントークンを明確に再利用し、関心の分離を維持します。また、ラッパーが許可するものにロックインされるのではなく、小さく焦点の絞られたピースを組み合わせるため、より柔軟です。

デザインシステムの一貫性についてはどうですか? — デザインシステムは、ラッパーを作成するのではなく、振る舞いとデザイントークン(色、フォント、間隔)を定義すべきです。開発者に、ネイティブ要素または最小限の抽象化を使用してそれらのトークンを構成させましょう。

このアプローチをチームにどのように説明すればよいですか? — 既存のコンポーネントへの批判としてではなく、将来のコンポーネントのための意思決定フレームワークとして位置づけてください。ドキュメント、テスト、メンテナンスなど、すべてのコンポーネントがもたらすコストに焦点を当てます。どのような種類の振る舞いが新しいコンポーネントの追加を正当化するかについて合意しましょう。

タグ

#react #frontend #architecture #components #webdev #designsystems #abstraction #codemaintenance

Free field guide

API Security Testing Checklist

A practical workflow for testing authentication, authorization, input handling, business logic, and evidence without losing track of scope.