lazy
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() {
// ...
}