12
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

TypeError: Cannot read properties of undefined (reading '***')エラーを解決してみた

Last updated at Posted at 2022-05-27

背景

業務ではまったエラーです。
個人的には、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などの値をとりうるのか、など考えることが増えた

参考

12
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?