【解決】Cannot read properties of undefined/null エラーの原因と修正
ReactでよくあるCannot read properties of undefined (reading 'xxx')エラーを解説。非同期データの読み込み前アクセス・undefinedなprops・配列メソッドのチェーンなど原因別に修正方法を示します。
このエラーを見たとき、「ちゃんとfetchしてるのになんでnullなんだ」という気持ちになりやすい。
TypeError: Cannot read properties of undefined (reading 'name')
TypeError: Cannot read properties of null (reading 'map')コードを直す前に、なぜこのエラーが起きるのかを理解しておいたほうがいい。原因がわかると、次に同じミスをしなくなるからだ。
Reactは「描いてからデータを取りに行く」
初めてReactを書いたとき、こういう順番で動くと思いやすい。
useEffectでfetchする → データが届く → 画面に表示される実際の流れはこうだ。
① コンポーネントが実行される(userはまだnull)
② Reactが画面を描く ← このタイミングでエラーが起きる
③ useEffectが走り始める
④ fetchが始まる(ネットワーク通信が走る)
⑤ データが届く
⑥ setUser(data) → ①に戻って再描画
⑦ 今度はuserに値があるので正しく表示されるuseEffectは描画の「後」に実行される。これはReactの仕様だ。なのでfetchを書いていても、最初の描画では必ずデータが空の状態で画面を作ろうとする。
このしくみを知っていれば、エラーの読み方が変わる。「nullのプロパティにアクセスした」ではなく、「データが届く前に描画が走った」という話だとわかる。
原因1:非同期データがまだ届いていない(最頻出)
上の流れを踏まえてコードを見ると、エラーの理由がわかる。
// ❌ なぜ失敗するのか
function UserProfile({ userId }: { userId: number }) {
const [user, setUser] = useState(null) // 初期値はnull
useEffect(() => {
fetchUser(userId).then(setUser) // これは描画の後に走る
}, [userId])
return <div>{user.name}</div> // 最初の描画でnull.nameを参照 → エラー
}「fetchしてるのになんでnullなんだ」ではなく「fetchが終わる前に描画が走っている」という話だ。
修正は、「データがない間は別の表示をする」こと。
// ✅ データが届くまでの表示を用意する
function UserProfile({ userId }: { userId: number }) {
const [user, setUser] = useState<User | null>(null)
useEffect(() => {
fetchUser(userId).then(setUser)
}, [userId])
if (!user) return <div>読み込み中...</div> // データがない間はここで止まる
return <div>{user.name}</div> // ここに来たとき、userは必ず存在する
}if (!user) return のパターンは「早期リターン」と呼ばれる。useEffectでデータ取得するコンポーネントではほぼ毎回登場する書き方なので、定番として覚えておいていい。
原因2:配列が undefined のまま .map() を呼んでいる
同じ「存在しないかもしれない値」の問題が、配列で起きるパターン。
// ❌ items が省略されたとき、.map() が存在しないのでエラー
function ItemList({ items }: { items?: string[] }) {
return (
<ul>
{items.map((item) => <li>{item}</li>)}
</ul>
)
}? がついているということは「渡されないこともある」ということだ。渡されなかったとき items は undefined で、undefined.map() は存在しない。
修正の方法は2つある。
// ✅ パターン1:「必ず配列として受け取る」ようにデフォルト値を設定する
function ItemList({ items = [] }: { items?: string[] }) {
return (
<ul>
{items.map((item, i) => <li key={i}>{item}</li>)}
</ul>
)
}// ✅ パターン2:オプショナルチェーンで「undefinedなら何もしない」
function ItemList({ items }: { items?: string[] }) {
return (
<ul>
{items?.map((item, i) => <li key={i}>{item}</li>)}
</ul>
)
}パターン1は「コンポーネントの中では配列として扱える」保証があるので、その後の処理がシンプルになる。パターン2は undefined のまま外に持ち出せるが、undefined のときは何も表示されない。どちらが正解かは要件次第だが、配列を描画するコンポーネントではパターン1のほうが見通しが良くなることが多い。
原因3:オブジェクトの途中がundefined
APIレスポンスで order.customer.address.city のように深い構造にアクセスするとき、途中が undefined だとエラーになる。
// ❌ customer が optional なのに直接 .name を参照している
type Order = {
id: number
customer?: { name: string; address?: { city: string } }
}
function OrderSummary({ order }: { order: Order }) {
return (
<div>
<p>{order.customer.name}</p> {/* customerがなければエラー */}
<p>{order.customer.address.city}</p> {/* addressがなければエラー */}
</div>
)
}「customerが来ないはずがない」と思っていても、APIは予期せず null や undefined を返すことがある。
オプショナルチェーン(?.)を使うと、途中が undefined ならそこで処理が止まって undefined を返す。エラーにならない。
// ✅ 途中がundefinedでもクラッシュしない
function OrderSummary({ order }: { order: Order }) {
return (
<div>
<p>{order.customer?.name ?? '未設定'}</p>
<p>{order.customer?.address?.city ?? '未設定'}</p>
</div>
)
}?.(オプショナルチェーン)と ??(nullish coalescing)はセットで使うことが多い。「undefinedかもしれないが、そのときはこの値を使う」という意図が一行で書ける。
原因4:useRefのcurrentにマウント前にアクセスしている
useRef でDOM要素を操作するとき、同じ「タイミング」の問題が起きる。
// ❌ コンポーネントの実行中はまだDOMが存在しない
function InputWithFocus() {
const inputRef = useRef<HTMLInputElement>(null)
inputRef.current.focus() // null.focus() → エラー
return <input ref={inputRef} />
}refにDOM要素がセットされるのは、Reactが画面を描いた後だ。returnの前(コンポーネントが実行されている最中)はまだ current が null のまま。
原因1と同じ構造だ。「描く前にアクセスしようとしている」。
// ✅ useEffectの中(描画後)で操作する
function InputWithFocus() {
const inputRef = useRef<HTMLInputElement>(null)
useEffect(() => {
inputRef.current?.focus() // 描画後に実行されるので安全
}, [])
return <input ref={inputRef} />
}TypeScriptで事前に気づく
tsconfig.json の strict: true を有効にしておくと、TypeScriptが「ここはnullかもしれない」と書いている時点で教えてくれる。
{
"compilerOptions": {
"strict": true
}
}このエラーで画面が真っ白になって原因を探すより、エディタ上で赤線を見つけるほうがずっと早い。TypeScriptの警告が出たとき「うるさい」と感じる場面もあるが、Cannot read properties 系のエラーはほぼTypeScriptが事前に教えてくれるものなので、警告を無視しないほうがいい。
このエラーを防ぐ1つの考え方
「データは遅れて来る」という前提で書く。
useEffectで取得する非同期データも、省略可能なpropsも、useRefで参照するDOMも——「ある瞬間は存在しない」という状態がある。その状態での動作をコードに書いておくことが、このエラーへの根本的な対処だ。