第23章:React.FCはもう古い?
ゴール:
React.FCを使わずに、関数の引数にそのまま型を当てる書き方に乗り換える。 メリット:いらないchildrenを勝手に許さない・ジェネリクスが書きやすい・読みやすい ✨
🗺️ まずは地図:型の流れをイメージしよう
🥊 React.FC と “いまどき”の直接注釈、なにが違うの?
旧来(React.FC を使う)
type CardProps = { title: string };
const Card: React.FC<CardProps> = ({ title }) => {
return <h2>{title}</h2>;
};
children?: ReactNodeが暗黙で付く(= 子要素を受け取れちゃう)。受け取るつもりがないのに OK になることがある😢- ジェネリクス(
<T>)を使うと 型推論がやや面倒になりやすい。
いまどき(引数に直接型を当てる)
type CardProps = { title: string };
function Card({ title }: CardProps) {
return <h2>{title}</h2>;
}
- 必要なPropsだけを明示。
childrenが欲しければ 自分で書く(後述)。 - ジェネリクスやユニオン、リテラル型などが素直に書ける。
- 見た目もシンプルで読みやすい👀
🧪 ハンズオン:3パターンで“直接注釈”を体に入れる!
src/componentsにファイルを作っていくよ。
① 子要素を受け取らない部品
// src/components/Header.tsx
type HeaderProps = { title: string; count: number };
export function Header({ title, count }: HeaderProps) {
return (
<header style={{ display: "flex", gap: 8, alignItems: "center" }}>
<h1 style={{ margin: 0 }}>🌸 {title}</h1>
<span>🔔 {count}</span>
</header>
);
}
✅
childrenを勝手に許可しないから、<Header>NG</Header>を書くと型エラーで守ってくれる🛡️
② 子要素(children)を受け取る部品
// src/components/Section.tsx
type SectionProps = {
label: string;
children?: React.ReactNode; // 自分で明示する!
};
export function Section({ label, children }: SectionProps) {
return (
<section style={{ border: "1px solid #eee", borderRadius: 12, padding: 12 }}>
<h2 style={{ marginTop: 0 }}>📎 {label}</h2>
{children}
</section>
);
}
✅ 欲しいときだけ
childrenを定義。設計がハッキリして読みやすい🌟
③ ジェネリクスを使うリスト部品(超使える!)
// src/components/List.tsx
type ListProps<T> = {
items: T[];
renderItem: (item: T) => React.ReactNode;
keySelector: (item: T) => string | number;
};
export function List<T>({ items, renderItem, keySelector }: ListProps<T>) {
return (
<ul>
{items.map((it) => (
<li key={keySelector(it)}>{renderItem(it)}</li>
))}
</ul>
);
}
✅
React.FCより ジェネリクスがスムーズ。Tが気持ちよく推論されるから、VS Code の補完も最高😍
🧩 親から使ってみる(総合演習)
// src/App.tsx
import { Header } from "./components/Header";
import { Section } from "./components/Section";
import { List } from "./components/List";
type Book = { id: number; title: string; author: string };
export default function App() {
const books: Book[] = [
{ id: 1, title: "React入門", author: "Hana" },
{ id: 2, title: "TypeScript図鑑", author: "Mio" },
];
return (
<main style={{ maxWidth: 560, margin: "24px auto", padding: "0 12px" }}>
<Header title="FC卒業ツアー" count={3} />
<div style={{ height: 12 }} />
<Section label="おすすめ本📚">
<List
items={books}
keySelector={(b) => b.id}
renderItem={(b) => (
<div>
<strong>{b.title}</strong> — {b.author}
</div>
)}
/>
</Section>
</main>
);
}
🧠 “直接注釈”の設計ポイント 5つ
-
子要素は自分で許可する
children?: React.ReactNodeを必要なときだけ定義。 -
ユニオン/リテラル型で入力ミスを防ぐ 例:
size: "sm" | "md" | "lg"は神🕊️ -
共通型をエクスポートして再利用
export type Book = {...}を他の部品でも共有して統一感UP。 -
ジェネリクスは“データの型”を1か所で握る
List<T>みたいに、親がTを決めると補完が賢い🧞♀️ -
戻り値の型注釈は基本いらない
JSX.Elementは推論されるので OK(読みやすさ重視)。
🆚 早見表:React.FC と 直接注釈
| 観点 | React.FC | 直接注釈(推奨) |
|---|---|---|
children | 暗黙で許可されがち | 必要な時だけ自分で書く |
| ジェネリクス | やや書きづらい | 素直で書きやすい |
| 可読性 | 型が2か所に分散しがち | 関数引数に集約して読みやすい |
| 学習コスト | 最初は楽に見える | 仕組みがシンプルで本質的 |
🔧 移行レシピ(置き換えテンプレ)
Before
type Props = { name: string };
const Hello: React.FC<Props> = ({ name }) => <p>Hi {name}</p>;
export default Hello;
After
type Props = { name: string };
export default function Hello({ name }: Props) {
return <p>Hi {name}</p>;
}
補足:どうしても
childrenを受けたい部品だけ👇
type Props = { name: string; children?: React.ReactNode };
export default function Hello({ name, children }: Props) {
return (
<div>
<p>Hi {name}</p>
{children}
</div>
);
}
🚨 ありがちミスと対処
-
子要素いらないのに入れちゃった
<Header title="..." count={1}>NG</Header> // ❌→
childrenを許可していないから 型エラー。設計ミスにすぐ気づける!🎉 -
childrenの型を細かくしたいReact.ReactElement(特定の部品だけ許可)やReactNode(幅広く許可)を使い分けよう。
📝 ミニ課題(10〜15分)🎯
-
既存の
UserCard: React.FC<UserCardProps>を、直接注釈のfunction UserCard({ ... }: UserCardProps)に書き換えよう。 -
Badgeコンポーネントを作成:- Props:
color: "red" | "green" | "blue"; children?: React.ReactNode - 表示:丸背景+中に
{children}。色はcolorで切り替え。
- Props:
-
ジェネリック
Grid<T>を作成:- Props:
items: T[]; renderItem: (item: T) => React.ReactNode - 2列グリッドで並べて表示(軽くインラインCSSでOK)。
- Props:
✅ 小テスト(○×)
React.FCを使うと、childrenを明示しなくても受け取れてしまう。childrenを受け取りたくないなら、直接注釈でchildrenを書かない設計にできる。- ジェネリクスの部品は
React.FCの方が書きやすい。 - 直接注釈でも
children?: React.ReactNodeを書けば、子要素を受け取れる。
答え:1○ / 2○ / 3× / 4○
🎬 まとめ
React.FCは便利そうに見えるけど、いらないchildrenまで許可してしまうなどの副作用がある。- Propsに直接型を当てるだけで、読みやすく・壊れにくいコードに💪
children・ジェネリクス・リテラル型など、必要なものだけ明示してクリアな設計にしよう🌈
次回(第24章)は、実際に
childrenの型をきれいに表現するコツをやっていくよ!お楽しみに〜🥳💕