Next.jsのnext/image
を使って画像を表示させている場合で、画像が404で取得できない場合の対処法を考えた
調べたところnext/image
にはonError
というpropがあり、エラーの場合にはここで設定したコールバック関数が呼ばれるのでこれを使えば良さそう
import Image from "next/image"; ... <Image src={url} onError={(e) => { // 何か処理 }} ... /> ...
ところで、ある外部APIで取得した画像パスのリストに対して、以下のようにページングできる(横スクロールはできない)ようにしている場合にどう処理するのがよいだろうか
次のような要件を満たすように処理できないか検討してみた
- 画像がみつかるまで次の画像を探す
- 全て404なら用意したNot Found画像を表示する
画像がみつかるまで次の画像を探す
ユーザビリティを考えると画像はなるべくあった方がよいので、できる限り画像を表示したい
外部APIで取得した画像リストのうち、たまたま1枚目がデッドリンクになっていただけかもしれないので、1枚目が取得できない場合は2枚目を、2枚目も取得できない場合は3枚目を、といったふうに画像を探してみる
import Image from "next/image"; ... const [activeImageIndex, setActiveImageIndex] = useState(0); ... <Image src={props.imageList[activeImageIndex]} onError={() => { if (activeImageIndex + 1 < props.imageList.length) { setActiveImageIndex(activeImageIndex + 1); } }} ... /> ...
これで、N枚目が取得できない場合はN+1枚目を探すようになった
しかし、画像が数十枚もあってほとんどデッドリンクの場合、勝手に何十枚ものリクエストを裏で送り続けてしまうことがわかった
枚数が少ない場合には採用してもよいかもだが、画像枚数が多い場合は良くなさそう
また、左右のページ送りと、下の白マルたちの扱いを考えなければならなかった
例えば、なかった画像はリストから排除していくような処理にした場合、画像がみつけている間リストの要素がなっていく様子がみえてしまう
なので、画像が見つかるまでは何かデフォルト画像(プレースホルダー)を出しておくいたほうが良いかもしれない
一応next/image
では、propに
placeholder="blur"
blurDataURL="base64エンコードされた画像"
を指定すれば、プレースホルダーが表示されるのだが1、エラーが起きた瞬間にプレースホルダーがなくなってしまったので断念した(onErrorのコールバック関数内でプレースホルダーを消さずに出し続けるといった処理もかけなかった)
なので、これは使わずに画像を覆う要素を自前で作り、画像のロードが上手くいったらその要素を消すといった処理を考える必要がありそう
もしやるとしたらonLoadingComplete
という画像のロードが完了したときに実行されるコールバック関数内でやると良いかも2
結論
N枚目が取得できない場合はN+1枚目を探すという処理はできなくはないが、画像の枚数が多い場合には考えることが多いため、やめておいた方がよさそう
また、自社のサイトでは、外部APIの傾向を調べたところ1枚目の画像が404なら残りもだいたい取得できないことがわかった
なので、N+1枚目を探すという処理はやめて、1枚目が取得できなければNot Found画像を表示するというシンプルな処理で調整したい