lazy を使うことで、あるコンポーネントが初めてレンダーされるまで、そのコードの読み込みを遅延させることができます。

const SomeComponent = lazy(load)

リファレンス

lazy(load)

lazy をコンポーネントの外部で呼び出し、遅延読み込みされる React コンポーネントを宣言します。

import { lazy } from 'react';

const MarkdownPreview = lazy(() => import('./MarkdownPreview.js'));

さらに例を見る

引数

  • load: Promise または thenable(then メソッドを持つ Promise のようなオブジェクト)を返す関数。返されたコンポーネントを初めてレンダーしようとするときまで React は load を呼び出しません。React が初めて load を呼び出した後、それが解決 (resolve) するのを待ち、解決した値の .default を React コンポーネントとしてレンダーします。返された Promise と解決済みの値は両方ともキャッシュされるため、React は load を 2 度以上呼び出しません。Promise が reject された場合、React はその理由を throw し、最も近いエラーバウンダリで処理できるようにします。

返り値

lazy は、ツリー内でレンダーできる React コンポーネントを返します。遅延コンポーネントのコードがまだ読み込まれていない間、レンダーしようとするとサスペンド (suspend) します。<Suspense> を使用して、読み込み中にローディングインジケータを表示します。


load 関数

引数

load は引数を受け取りません。

返り値

Promise または何らかの thenable(then メソッドを持つ Promise のようなオブジェクト)を返す必要があります。最終的に、有効な React コンポーネント型、つまり例えば関数、memo、または forwardRef コンポーネントのようなものを .default プロパティとして持つオブジェクトに解決される必要があります。


使用法

サスペンスを使ったコンポーネントの遅延読み込み

通常、コンポーネントは静的な import 宣言を使ってインポートします。

import MarkdownPreview from './MarkdownPreview.js';

このコンポーネントのコードの読み込みを、初めてレンダーされるときまで遅延させるには、このインポートを以下のように置き換えます。

import { lazy } from 'react';

const MarkdownPreview = lazy(() => import('./MarkdownPreview.js'));

このコードはダイナミック import() を用いており、あなたのバンドラやフレームワークからのサポートが必要かもしれません。このパターンを用いる場合は、遅延インポートしようとしているコンポーネントが default でエクスポートされている必要があります。

コンポーネントのコードがオンデマンドで読み込まれるようになったので、読み込みの最中には何を表示するべきかを指定する必要があります。これは、遅延コンポーネントまたはその親のいずれかを <Suspense> バウンダリでラップすることで行うことができます。

<Suspense fallback={<Loading />}>
<h2>Preview</h2>
<MarkdownPreview />
</Suspense>

この例では、MarkdownPreview のコードはレンダーしようとするまで読み込まれません。もし MarkdownPreview がまだ読み込まれていない場合、その代わりに Loading が表示されます。チェックボックスをオンにしてみてください。

import { useState, Suspense, lazy } from 'react';
import Loading from './Loading.js';

const MarkdownPreview = lazy(() => delayForDemo(import('./MarkdownPreview.js')));

export default function MarkdownEditor() {
  const [showPreview, setShowPreview] = useState(false);
  const [markdown, setMarkdown] = useState('Hello, **world**!');
  return (
    <>
      <textarea value={markdown} onChange={e => setMarkdown(e.target.value)} />
      <label>
        <input type="checkbox" checked={showPreview} onChange={e => setShowPreview(e.target.checked)} />
        Show preview
      </label>
      <hr />
      {showPreview && (
        <Suspense fallback={<Loading />}>
          <h2>Preview</h2>
          <MarkdownPreview markdown={markdown} />
        </Suspense>
      )}
    </>
  );
}

// Add a fixed delay so you can see the loading state
function delayForDemo(promise) {
  return new Promise(resolve => {
    setTimeout(resolve, 2000);
  }).then(() => promise);
}

このデモは人為的に遅延させて読み込まれます。もう一度チェックボックスをオフにしてからオンにすると、Preview はキャッシュされているので、ローディング状態は表示されません。再度ローディング状態を表示するには、サンドボックスの “Reset” をクリックしてください。

サスペンスを使ったローディング状態の管理についてもっと学ぶ


トラブルシューティング

lazy コンポーネントの state が予期せずリセットされる

lazy コンポーネントを他のコンポーネントの内部で宣言しないでください。

import { lazy } from 'react';

function Editor() {
// 🔴 Bad: This will cause all state to be reset on re-renders
const MarkdownPreview = lazy(() => import('./MarkdownPreview.js'));
// ...
}

代わりに、常にモジュールのトップレベルで宣言してください。

import { lazy } from 'react';

// ✅ Good: Declare lazy components outside of your components
const MarkdownPreview = lazy(() => import('./MarkdownPreview.js'));

function Editor() {
// ...
}