【6章】React実践演習の振り返り
公開: 2026年01月03日
React実戦の課題を通して非常に学びが多かったので、自分なりの備忘録として記録に残そうと思います。(理解不十分で記載が誤っている箇所があるかもしれませんが。。)
※コードについての記載があるため、これから取り組む方は実装後に読んでいただく方が良いかもしれません。
課題で提示された要件に加え、今回は個人的な学習テーマとして、プラスアルファの設計にも挑戦してみました。
(※課題の必須要件ではなく、あくまで自分なりのこだわりとして取り組んだ内容としてまとめております。ぶべさんのレビューにはとても感謝しました😭)
「再利用性」と「責務の分離」の両立
汎用的なUIパーツ(Atoms)と、実際の表示やロジックを持つ機能単位を明確に切り分けること。
普段行っているレベル感でのアクセシビリティとUXを意識
本業で行っていることをReactでどう表現できるか試したかったので、aria属性の制御やリアルタイムバリデーション、Hookを使ったエラーハンドリングの共通化など、実務を想定した+αの設計を行いました。
課題1〜3については、基本的に4章の書籍で学んだことを実践し、アウトプットを通じてSPAやルーターの仕組みを具体化することができました。
今回は、特に学びが深かった「課題4:問い合わせフォームの作成」についてまとめます。
普段からAstro.jsでのコーディングや、PHPでのフォーム開発を行っているため、大まかな流れはイメージできていましたが、「React 19以降のモダンな書き方」や「Propsの正しい継承」については、実戦形式のレビューを通して初めて理解できた部分が多かったです。
具体的に学んだことは、主に以下の3点です。
コンポーネントの見た目を固定しつつ、親から渡されるclassNameや標準属性(type, id, disabledなど)を、いかにノイズなくHTMLタグへ「透過(スプレッド)」させるか。
特に、独自Props(errorなど)を事前に抽出し、残りの標準属性のみを {...props} で流し込む手法には設計としての美しさを感じました。
ぶべさんからご提示いただいた以下の記事からも、かなり理解を深められました。
参考:https://zenn.dev/takepepe/articles/atoms-type-definitions
静的なコーディングとは異なり、入力状態に応じて変化するフォームにおいて、スクリーンリーダーが正しくエラーを検知できるようaria-invalid等の属性とidを動的に管理する設計は、苦戦しましたが、その分学びも多かったです。
React 19からrefが通常のPropsとして扱えるようになったことで、これまでforwardRefを使って書いていた記述が不要になりました。(※ぶべさんのご指摘で知る)
今回の実装ではreact-hook-formを使用しましたが、バリデーションエラーが発生した際に「最初のエラー箇所へ自動でフォーカスを当てる」といったUX向上や、アクセシビリティ対応のためにDOMへ直接アクセスしたい場合、記述が劇的にスッキリすることを学びました。
参考:https://ja.react.dev/reference/react/forwardRef
上記のポイントを意識して実装したInputパーツです。
// src/components/ui/Input.jsx
export default function Input({ error, className = "", ref, ...props }) {
return (
<input
ref={ref} // 外部(hook-form等)からの操作を受け入れる
{...props} // 標準属性を透過
aria-invalid={error ? "true" : "false"}
aria-describedby={error && props.id ? `${props.id}-error` : undefined}
className={`w-full border rounded-lg px-4 py-4 outline-none transition shadow-sm ${
props.disabled ? "bg-gray-100 text-gray-400 border-gray-200" : ""
} ${
error
? "border-red-700 focus:ring-1 focus:ring-red-700"
: "border-gray-300 focus:border-blue-500 focus:ring-1 focus:ring-blue-500"
} ${className}`} // 外部からのclassNameをマージ
/>
);
}このパーツを組み合わせて、フォーム側は以下のように構成しました。
// src/components/contact/ContactForm.jsx (抜粋)
<FormField htmlFor="name" label="お名前" error={errors.name}>
<Input
id="name"
autoComplete="name"
disabled={isSubmitting}
error={errors.name}
{...register("name", CONTACT_VALIDATION.name)}
/>
</FormField>普段Astro.jsを触っていると、コンポーネントがいかに「UIの表示(ピュアなPropsの受け取り)」に徹するべきかを意識しますが、React 19の進化もその設計思想をよりシンプルに、より洗練させていく方向性で、触っていて非常に楽しかったです。
書籍やドキュメントで見ている以上に、実際に手を動かして「独自Propsだけを抜き出して、Omitする」処理を書いたことで、ComponentPropsを使ったタグ属性の継承の仕組みが一気に理解できました。
Atomsの純粋化
UIパーツは余計なラッパーを持たず、標準属性を {...props} で受け入れることで再利用性を高める。
className のマージ
Tailwind CSS等を使用する場合、外部からの余白調整(mt-10など)を許容する設計が実務では必須であると再認識。
標準への準拠
独自プロパティでガチガチに固めるより、標準属性を素直に受け入れる方が、結果として柔軟で使い勝手の良いコンポーネントになる。
今回の課題実装は「早くアプリ開発がしたい!」と感じるほど楽しかったです。
また、アウトプットする大切さも学んだので、毎日少しでもアウトプットを行おうと感じました。
全体の進捗はまだまだこれからですが、隙間時間を活用して、5月のGW明けにはオリジナルアプリ開発に着手できるよう計画を立てて進めていきたいです!!
