React で簡単に遅延読み込みを実装できるライブラリを作りました

はじめに

どうも、さぃと (@saitoeku3) です。

突然ですが、あなたは自作のライブラリを公開したことがありますか?
僕は公開したいと思いつつもなかなかできずに年を越してしまいそうだったので、思い切って公開しちゃいました。

ネタは最近よく使っている遅延読み込みと Gatsby.js + Contentful 関連の2つあったのですが、今回は前者の方です。(後者もそのうち公開します)

作ったもの

github.com

返り値の ref を任意のエレメントの ref に渡すだけで遅延読み込みをしてくれるカスタムフックを利用できるライブラリです。React での使用を想定しています。

今までにもいくつか遅延読み込みをしてくれるライブラリはあったのですが、コンポーネントベースで利用するものが多くて使いづらかったという問題を解決しています。(強引な押し売り)

特に差別化できると思っているのは、isLoaded という読み込みが終わると false から true に切り替わる変数を使えるところで、プレースホルダーを出して UX を改善するときにも使えます。他にも CSSbackground-image に対応していたりと便利なライブラリになっています。

対応しているエレメントは imgiframevideoaudio の4つで background-image についてはその他の HTML エレメント全てで使えるはずです。

import React from 'react'
import useLazyloadRef from 'use-lazyload-ref'

const Component = ({ url }) => {
  const [ref, isLoaded] = useLazyloadRef()

  return (
    <>
      {isLoaded || <Skeleton />}
      <img ref={ref} data-src={url}>
    </>
  )
}

内部実装

画面に入っているかの検知は IntersectionObserverref の使い方は「コールバック形式の ref」を使用しました。やっていることは割とシンプルで次のようになっています。

  1. マウント時に任意のエレメントを IntersectionObserver で監視させる
  2. 画面に入ったことを IntersectionObserver が検知する
  3. 任意のエレメントの data-srcsrc に渡す
  4. 読み込みが終わると onload で読み込みが終了したことを検知して isLoadedtrue にする

ただし CSSbackground-image を遅延読み込みする場合は少し特殊なことをします。理由は CSS だと onload で読み込みの検知ができないからです。そのため、次のように1度 img を生成して img で読み込んだ後に background-image を更新するという手を取りました。

const url = target.dataset.src || ''
const img = new Image()
img.src = url
img.onload = () => {
  target.style.backgroundImage = `url(${url})`
  setIsLoaded(true)
}

参考

おわりに

今回は自作ライブラリの紹介と解説をしました。

フロントエンドの UX 改善やパフォーマンスチューニングに使えるものなので、そのあたりに興味のある方は実際に使ってみていただけると嬉しいです。不具合や質問があればすぐに対処するので連絡ください。

最後に GitHub でスターをつけていただけると励みになるのでお待ちしてます。

github.com