Reactでは src={src} だと値が空のときバグが発生する

Reactの src={src} とは

Reactではsrc属性の値を以下のようなコードで動的に変更することができます。

App.tsx
// 1〜9のランダムの数字を生成
const randomNum = Math.floor(Math.random() * 9) + 1;

function App() {
  const src = `https://placedog.net/400x300/?id=${randomNum}/`

  return (
    <>
      <img src={src} width="400" height="300" alt="" />
    </>
  )
}

export default App

Reactでidが1〜9のランダムな犬の画像を表示するサンプル

Reactの基本中の基本のコードですが、propsやAPIなどから値を取得した際に値が空の場合にバグが発生する可能性があることを知らない人が多いので、このような書き方をする際は注意が必要です。

src={src} の値が空だとバグが発生する危険あり

もしも、取得したsrcの値が空で以下のようになった場合は、ブラウザは現在のページURLを再リクエストします。

App.tsx
<img src="" width="400" height="300" alt="" />

つまり…

  1. ページ表示
  2. imgが同じURLをGET
  3. サーバーにもう一回アクセス

…というリクエストが発生します。

Reactはこれを危険な挙動として検知して、Consoleに警告を表示します。

An empty string ("") was passed to the src attribute. This may cause the browser to download the whole page again over the network. To fix this, either do not render the element at all or pass null to src instead of an empty string.

An empty string ("") was passed to the src attribute. This may cause the browser to download the whole page again over the network. To fix this, either do not render the element at all or pass null to src instead of an empty string.

なぜ危険なのかというと、ページを毎回もう一度取得するため、以下の問題を引き起こすからです。

  • パフォーマンス悪化
  • サーバー負荷増大
  • バグ発生の可能性

SSRだと無限ループの原因になる

Next.jsなどのSSRの場合は、サーバーに同じページリクエストを繰り返す無限ループになるので、問題はさらに深刻です。

HTMLに img src=""

サーバーに同じページリクエスト

また img src=""

サーバーに同じページリクエスト

src={src} の値が空の可能性があるときの修正方法

src={src} の値に空が入る可能性がある場合は、以下のようにsrcに「 || undefined」を追記すれば、src属性が消えて <img width="400" height="300" alt="" /> になるので、srcの値が空になる問題を回避できます。

これはsrc属性の値にnullかundefinedが渡された場合はsrc属性が除去されるからです。

nullだとTypeScriptでは警告が表示されるため、undefinedにしています。

App.tsx
<img src={src || undefined} width="400" height="300" alt="" />

以下のようにsrcが空ならimgタグ自体を表示しない書き方もありますが、srcが空になっていることに気づきにくかったり、画面のレイアウトが崩れるリスクがあるので、この書き方はやめたほうが良いです。

App.tsx
{src && <img src={src} />}

以下にsrcの値が空のときにsrc属性がimgタグから除去されるサンプルページを作成しましたので、ブラウザで確認してみてください。

App.tsx
import { useEffect, useState } from 'react'

function App() {
  const [src, setSrc] = useState<string | null>(null)

  useEffect(() => {
    let objectUrl: string | null = null
    fetch('https://dummyjson.com/image/400x300')
      .then((response) => response.blob())
      .then((blob) => {
        objectUrl = URL.createObjectURL(blob)
        // setSrc(objectUrl)
        setSrc('')
      })
      .catch(console.error)

    return () => {
      if (objectUrl) URL.revokeObjectURL(objectUrl)
    }
  }, [])

  return (
    <>
      <img src={src || undefined} width="400" height="300" alt="" />
    </>
  )
}

export default App

Reactでsrcの値が空のときはsrc属性がimgタグから除去されるサンプル