🦥

【パフォーマンス】Reactで優先度低めでごにょごにょするHooks

2023/03/06に公開

こんにちは。ぬこすけです。

コンポーネントの画面描画には優先度があるでしょう。
ECサイトを想像してみてください。
例えばユーザーの目に止まる商品の画像はできるだけ早く描画させて、ちょっとしたアイコンは遅く描画でも良いでしょう。

ネット通販で買い物をしている女性

この記事では React で「他のコンポーネントの画面描画を優先させて、このコンポーネントは優先度を低く画面を表示したい」というようなケースが出てきたときに使える Hooks を紹介 します。

実装例

いきなりですが先に実装例を挙げます。

import { useEffect, useState } from 'react';

// ページのリソースが完全に読み込まれたことを通知する Hooks
const useDocumentLoadCompleted = (): boolean => {
  const [isLoaded, setIsLoaded] = useState(false);
  const loaded = () => setIsLoaded(true);

  useEffect(() => {
    if (document.readyState === 'complete') {
      loaded();
      return;
    }
    // またページが完全に読み込まれていないので、読み込みが完了したら処理させる
    window.addEventListener('load', loaded);
    return () => {
      window.removeEventListener('load', loaded);
    };
  }, []);

  return isLoaded;
};

export default useDocumentLoadCompleted;

この useDocumentLoadCompleted Hooks は例えば次のように使います。

export const LowPrioriryImage: FC = () => {
  const isLoaded = useDocumentLoadCompleted();
  return isLoaded ? <Image /> : null;
}

解説

まずは Hooks の方を見てみましょう。

  useEffect(() => {
    if (document.readyState === 'complete') {
      loaded();
      return;
    }
    // またページが完全に読み込まれていないので、読み込みが完了したら処理させる
    window.addEventListener('load', loaded);
    return () => {
      window.removeEventListener('load', loaded);
    };
  }, []);

肝となる実装はこの useEffect のところでしょう。

https://qiita.com/nuko-suke/items/50ba4e35289e98d95753#documentreadystate-や-load-イベントを使ってページが完全に読み込まれたら処理を開始する

こちらの記事にも書いてありますが、 document.readyStatewindow.addEventListener('load', callback) を組み合わせることで、 CSS や画像などのサブリソースを含めて完全にページの読み込みが完了してから処理を開始させることができます

export const LowPrioriryImage: FC = () => {
  const isLoaded = useDocumentLoadCompleted();
  return isLoaded ? <Image /> : null;
}

Hooks の利用側のコンポーネントは、「 CSS や画像など含めて完全にページが読み込まれた後に Image コンポーネントを画面に描画する 」というような実装になっています。

これは簡単な例ですが、例えば React.lazyReact.Suspense を組み合わせるとより効力を発揮 するでしょう。

import { lazy, Suspense } from 'react';

const Image = lazy(() => import('./Image'));

export const LowPrioriryImage: FC = () => {
  const isLoaded = useDocumentLoadCompleted();
  return (
    <Suspense>
      {isLoaded && <Image />}
    </Suspense>
  )
}

Image コンポーネントは初回に配信されるスクリプトからは除外されるために 初回の読み込みは早くなる でしょう。
CSS や画像など含めて完全にページが読み込まれて初めて Image のスクリプトを読み込み、画面にコンポーネントを描画します。

まとめ

サクッとではありますが、 CSS や画像など含めてページが完全に読み込まれたことを通知する Hooks を作成し、コンポーネント描画の優先度を下げる 実装例を紹介しました。

他にもパフォーマンスチューニングに興味がある方は次の記事でアホみたいな量をご紹介しているので、ぜひ覗いてみてください!

https://qiita.com/nuko-suke/items/50ba4e35289e98d95753

ここまでご覧いただきありがとうございました! By ぬこすけ

Discussion