こんにちは。チームのコードレビューをしていて、未だに「あ、ここでそれやっちゃうんだ」と頭を抱えたくなる瞬間がある。それが今回取り上げる Propsのデフォルト値の扱い方 だ。
Reactを触り始めて最初に覚えるのが `defaultProps` だったりする。クラスコンポーネントの時代からReactを触ってきたシニア層ほど、この呪縛に囚われがちだ。しかし、現代のモダンなReact(特にTypeScriptを組み合わせた環境)において、`defaultProps` はすでにレガシーな遺物になりつつある。関数コンポーネントのデフォルト引数(Default Parameters)を使うのが、今の現場のスタンダードだ。
今回は、なぜデフォルト引数を使うべきなのか、TypeScriptとどう組み合わせるのが正解なのか、ブラウザの裏側の動きや実践的なパターンを交えて、徹底的に解説していこう。
—
なぜ `defaultProps` ではなく「デフォルト引数」なのか?
まず大前提として、関数コンポーネントにおけるPropsの初期値設定は、JavaScriptの標準機能であるデフォルト引数を使うべきだ。
Reactの公式(TypeScript環境)でも、関数コンポーネントの `defaultProps` は将来的に非推奨になる方向で議論が進んでいる。理由はいくつかあるが、最大の理由は 「TypeScriptの型推論と相性が最悪だから」 だ。
`defaultProps` を使うと、オプショナル(省略可能)なはずのプロパティにまでデフォルト値が補完されることで型が複雑化し、Generics(ジェネリクス)を使ったコンポーネントを書くときに型が完全に崩壊する。
一方、JavaScript/TypeScriptのデフォルト引数を使えば、コンポーネントが呼び出された瞬間にパラメータレベルで値が補完されるため、型との親和性が極めて高い。
ブラウザの裏側で何が起きているか?
ここで一度、Reactのレンダリングパイプラインとブラウザの挙動を軽くおさらいしておこう。
私たちがJSXで `
コンポーネントが呼び出される際、Reactは渡されたPropsのオブジェクトを関数の第1引数として渡す。ここで、JavaScriptのデフォルト引数構文(ES2015で導入された機能)が評価される。
// ブラウザが解釈するJavaScriptレベルの挙動(イメージ)
function UserCard(props) {
// props.role が undefined の場合のみ “guest” が代入される
const { name, role = “guest” } = props;
return …
}
この仕組みは、V8などのJavaScriptエンジンにとっても非常に最適化しやすく、余計なオブジェクトの再生成やプロパティの書き換えが発生しないため、ランタイムのパフォーマンスにおいても極めて健全だ。
—
現場で即戦力になる!型安全なデフォルト引数の実装パターン
百聞は一見にしかず。中級から一歩抜け出すための、実務でそのまま使えるコードパターンを見ていこう。
今回は、企業の管理画面などでよく見かける「ユーザープロファイルカード」を例にする。`role` や `isActive` といったステータスにデフォルト値を持たせるケースだ。
import React from ‘react’;
// 1. Propsの型定義(ここでは全てオプショナルにする必要はない)
// 必須のものは必須にし、デフォルト値を設定するものはオプショナル(?)にする
export interface UserProfileProps {
/ ユーザーの一意なID(必須) /
userId: string;
/ 表示名(必須) /
name: string;
/ ユーザーの権限(省略時は “member”) /
role?: ‘admin’ | ‘editor’ | ‘member’;
/ アカウントの有効状態(省略時は true) /
isActive?: boolean;
/ アバター画像のURL(省略時はデフォルト画像) /
avatarUrl?: string;
/ カードクリック時のハンドラー(任意) /
onSelect?: (userId: string) => void;
}
export const UserProfileCard: React.FC
userId,
name,
// 2. 分割代入のデフォルト引数で初期値を定義する
role = ‘member’,
isActive = true,
avatarUrl = ‘https://example.com/default-avatar.png’,
onSelect,
}) => {
// 3. ここに到達した時点で、role や isActive は確実に型が確定している(undefinedではない)
// TypeScriptは賢いので、デフォルト値があるおかげで「string | undefined」ではなく「string」として扱ってくれる。
const handleClick = () => {
if (onSelect) {
onSelect(userId);
}
};
return (
{name}
権限: {role}
ステータス: {isActive ? ‘有効’ : ‘無効’}
);
};
このパターンの美しさと強み
1. 型ガードの恩恵:
`role` は `?` 付きでオプショナルにしてあるが、デフォルト引数 `role = ‘member’` を指定しているため、コンポーネント内のロジックでいちいち `role ? role : ‘member’` のようなフォールバックを書く必要が一切ない。しかも、TypeScriptは `role` が `undefined` にならないことを保証してくれるため、文字列メソッドや比較処理も安心して書ける。
2. コンポーネント利用時のDX(開発者体験):
親コンポーネントから呼び出す際、デフォルト値があるプロパティ(`role` や `isActive`)をイチイチ指定しなくて済むため、コードが劇的にスッキリする。
// 親コンポーネントでの使用例
const App = () => {
return (
);
};
—
陥りがちな罠:オブジェクトの分割代入と「デフォルト値のスコープ」
ここで、中級エンジニアでもうっかりやりがちなバグのパターンについても共有しておこう。それは「部分的なオブジェクトのデフォルト値」を扱うときだ。
例えば、Propsの中にさらに設定用のオブジェクト(例: `settings?: { theme?: string; notifications?: boolean }`)が含まれている場合、単純なデフォルト引数ではハマることがある。
❌ やりがちなミス
type Settings = {
theme?: string;
notifications?: boolean;
};
type Props = {
settings?: Settings;
};
// これだと、settings自体が渡されなかった場合にエラーになる!
const MyComponent = ({ settings: { theme = ‘light’, notifications = true } }: Props) => {
// 親から settings が渡されなかった場合(undefinedの場合)、
// 構造化代入の `settings: { … }` で undefined のプロパティにアクセスしようとするため、
// 「TypeError: Cannot destructure property ‘theme’ of ‘undefined’ or ‘null’.」が爆誕する。
return
;
};
⭕ 正しい回避策
オブジェクト全体が `undefined` の可能性がある場合は、引数全体でのデフォルト値 と プロパティごとのデフォルト値 を二重に定義するか、あらかじめデフォルトオブジェクトを用意するのが鉄則だ。
type Settings = {
theme?: string;
notifications?: boolean;
};
type Props = {
settings?: Settings;
};
const MyComponent = ({ settings = {} }: Props) => {
// 安全にオブジェクトを取り出してから、内部の値を展開・評価する
const { theme = ‘light’, notifications = true } = settings;
return
;
};
さらに洗練されたアプローチとして、コンポーネントの外側に「デフォルト設定の定数(FALLBACK_SETTINGS)」を切り出すのも、実務の現場では好まれるテクニックだ。コードの可読性がグッと上がり、テストもしやすくなる。
—
まとめ:シニアから後輩たちへ伝えたいこと
ReactにおけるPropsの初期値設定は、一見するとただの「お作法」に見えるかもしれない。しかし、ここを適当に済ませていると、コンポーネントが肥大化したときに型エラーの山に悩まされたり、予期せぬ `undefined` 起因のバグ(Cannot read properties of undefined)に足元をすくわれることになる。
- `defaultProps` は使わず、JavaScriptのデフォルト引数を使おう。
- TypeScriptの型定義とデフォルト引数を組み合わせることで、堅牢かつ書きやすいコードになる。
- ネストしたオブジェクトのデフォルト値には「空のオブジェクトフォールバック」を忘れない。
この基本を徹底するだけで、君が書くコードの信頼性は一段と跳ね上がる。ぜひ、明日からのコードレビューや実装で意識してみてほしい。現場からは以上だ。

コメント