背景
業務ではまったエラーです。
個人的には、TypeError: Cannot read properties of undefined (reading '***')
というエラーは自分のような経験の浅い人にとっては原因を見つけづらいエラーの一つだと思い、今回記事にまとめました。
*コードはフェイクですので少しわかりづらいかも。
原因から考えると解決方法は複数ありそうですが、一つの参考になれば嬉しいです。
前提(今回のストーリー)
例えばユーザー登録されたユーザーが複数いて、admin権限を持つユーザーが一人だけという内容の以下のデータがあり、このデータをAPIで取得しているとする。
// APIにてusersを取得したとする
users = [
{ id: 1,
userName: 'suzuki',
admin: true
},
{ id: 2,
userName: 'tanaka',
admin: false
},
{ id: 3,
userName: 'sasaki',
admin: false
},
]
上記の中のadmin権限を持つユーザー情報だけを表示したいときに、以下の状態だとエラーが出る。
export const App = () => {
// この時点でusersは空配列
let users = []
// ①この時点でユーザーデータ取得のAPIが走る(useEffectを用いたdispatch)
// adminUserを抽出
const adminUser = users.filter(user => {
return user.admin === true
})
// 一度目のレンダリング(この時点では①の取得が完了していない)
// ↓↓の時点でエラーが発生
const adminUserDisplay = () => {
return adminUser[0].userName
}
// ②APIによるユーザーデータ取得完了した時点で、このタイミングでadminUserDisplayを実行したいが、上記エラーで止まってしまっているため実行できない。
return (
<>
<div className="App">
<h1>adminUserを表示</h1>
<div>{adminUserDisplay()}</div>
</div>
</>
);
}
少しわかりづらいかもしれないが、上記のようにadminUserを取得する処理が上記コンポーネントが描画されるまでに間に合わず、users
が空になっていたことで今回のエラーTypeError: Cannot read properties of undefined (reading '***')
が発生した。
直訳すると、「タイプエラー: 未定義のプロパティは読み込めません。」
*タイプエラーのタイプは型という意味
// console.logでadminUserを出力すると空配列が出力される
console.log(adminUser)
// >> []
解決方法
上記の場合、usersデータを取得できる前とできた後で2回レンダリングされる。
取得できる前にエラーが発生してしまい、2回目のレンダリングが走らない状況だった。
なので、1回目のエラー回避をしてあげれば2回目のレンダリングまでたどり着けるはず。ということでadminUserDisplayメソッドに以下の条件を追加することで解決できた。
if (typeof adminUser[0] !== 'undefined') {
return <div>{currentMainUser[0].id}</div>
typeof演算子(undefined判定)
if (typeof adminUser[0] !== 'undefined')` 要はadminUser[0]がundefinedじゃない場合にreturnするという簡単な条件。 単純に
adminUser[0] !== 'undefined'`だと以下のエラーとなる。
if (t === 'undefined') {
console.log('t is undifined!!')
}
// >> Error: t is not defined
undefinedは'undefined'だとundefindだと認識してくれない。
伝わるだろうか?'undefined'は文字列のundefinedとして認識されるので、状態としてのundefinedではない。
Primitive(プリミティブ)
-
undefined
のようにメソッドを持たないデータのことをプリミティブ
と呼ぶそうです。 - 文字列、数値、BigInt、真偽値、undefined、シンボルの6種類
- プリミティブは全てイミュータブル(変更不可)
→つまり、consoleやエラーで吐かれるundefined
は文字列としてのundefinedではないんですね。
そこで、typeofを使うことでundefined状態なのかを判定できる。
if (typeof t === 'undefined') {
console.log('t is undefined!!')
}
// >> "t is undefined!!"
typeof
演算子の後に変数を置くことで、変数の型を返してくれる。
*変数だけじゃなく、数値、文字列でも型を返してくれる
console.log(typeof t)
// >> "undefined"
console.log(typeof 10)
// >> "number"
console.log(typeof "hoge")
// >> > "string"
懸念点
一旦これでエラーを回避することはできた。
しかし、xがnull, falseなどのtypeof x === 'undefinded'
が好ましくない場合もあるので注意が必要。
学び
-
TypeError: Cannot read properties of undefined (reading '***')
の難しさはなぜundefinedになっているかを考える必要がある部分にある - undefinedの判定にはtypeofを使う必要がある
- null, falseなどの値をとりうるのか、など考えることが増えた
参考