Reactを使用した非同期データの読み込み、遅延読み込み、コード分割の処理方法
著者は、 Creative Commons を選択して、 Write forDOnationsプログラムの一環として寄付を受け取りました。
序章
JavaScript Web開発者として、非同期コードを使用すると、コードの一部を実行しながら、他の部分がデータを待機または解決することができます。 これは、アプリの重要な部分がレンダリングされる前に、重要性の低い部分を待つ必要がないことを意味します。 非同期コードを使用すると、新しい情報を要求して表示することでアプリケーションを更新することもでき、長い関数や要求がバックグラウンドで処理されている場合でもユーザーにスムーズなエクスペリエンスを提供します。
React 開発では、非同期プログラミングには固有の問題があります。 たとえば、React 機能コンポーネントを使用する場合、非同期関数は無限のループを作成する可能性があります。 コンポーネントがロードされると、非同期関数を開始できます。非同期関数が解決されると、再レンダリングがトリガーされ、コンポーネントが非同期関数を呼び出すことができます。 このチュートリアルでは、useEffect と呼ばれる特別なフックを使用してこれを回避する方法を説明します。このフックは、特定のデータが変更された場合にのみ関数を実行します。 これにより、各レンダリングサイクルではなく、意図的に非同期コードを実行できるようになります。
非同期コードは、新しいデータの要求だけに限定されません。 Reactには、遅延読み込みコンポーネント、またはユーザーが必要な場合にのみそれらを読み込むためのシステムが組み込まれています。 Create ReactAppのデフォルトのwebpack構成と組み合わせると、コードを分割して、大きなアプリケーションを必要に応じてロードできる小さな部分に縮小できます。 ReactにはSuspenseと呼ばれる特別なコンポーネントがあり、ブラウザーが新しいコンポーネントをロードしている間、プレースホルダーを表示します。 Reactの将来のバージョンでは、Suspenseを使用して、レンダリングをブロックせずにネストされたコンポーネントにデータをロードできるようになります。
このチュートリアルでは、河川に関する情報を表示し、setTimeoutを使用してWebAPIへのリクエストをシミュレートするアプリを作成することにより、Reactで非同期データを処理します。 このチュートリアルを終了すると、useEffectフックを使用して非同期データをロードできるようになります。 また、データ解決の前にコンポーネントがアンマウントされた場合でも、エラーを発生させることなくページを安全に更新できます。 最後に、コード分割を使用して、大きなアプリケーションを小さな部分に分割します。
前提条件
- Node.jsを実行する開発環境が必要になります。 このチュートリアルは、Node.jsバージョン10.20.1およびnpmバージョン6.14.4でテストされました。 これをmacOSまたはUbuntu18.04にインストールするには、Node.jsをインストールしてmacOSにローカル開発環境を作成する方法またはのPPAを使用したインストール]セクションの手順に従います。 Ubuntu18.04にNode.jsをインストールする方法。
- Create React App でセットアップされたReact開発環境で、不要なボイラープレートが削除されています。 これを設定するには、ステップ1 —Reactクラスコンポーネントの状態を管理する方法のチュートリアルの空のプロジェクトを作成します。 このチュートリアルでは、プロジェクト名として
async-tutorialを使用します。 useStateやuseReducerフックなどのReactイベントとフックを使用します。 イベントについては、 React チュートリアルでDOMおよびウィンドウイベントを処理する方法、およびReactコンポーネントのフックで状態を管理する方法でフックを学ぶことができます。- また、JavaScriptとHTMLの基本的な知識も必要です。これは、HTMLシリーズでWebサイトを構築する方法およびJavaScriptでコーディングする方法にあります。 CSSの基本的な知識も役立ちます。これは、 Mozilla DeveloperNetworkで見つけることができます。
ステップ1—useEffectを使用した非同期データのロード
このステップでは、useEffectフックを使用して、非同期データをサンプルアプリケーションにロードします。 フックを使用して、不要なデータフェッチを防ぎ、データの読み込み中にプレースホルダーを追加し、データが解決されたときにコンポーネントを更新します。 この手順を完了すると、useEffectを使用してデータをロードし、解決時にuseStateフックを使用してデータを設定できるようになります。
このトピックを探索するために、世界で最も長い川に関する情報を表示するアプリケーションを作成します。 外部データソースへのリクエストをシミュレートする非同期関数を使用してデータをロードします。
まず、RiverInformationというコンポーネントを作成します。 ディレクトリを作成します。
mkdir src/components/RiverInformation
テキストエディタでRiverInformation.jsを開きます。
nano src/components/RiverInformation/RiverInformation.js
次に、プレースホルダーコンテンツを追加します。
async-tutorial / src / components / RiverInformation / RiverInformation.js
import React from 'react';
export default function RiverInformation() {
return(
<div>
<h2>River Information</h2>
</div>
)
}
ファイルを保存して閉じます。 次に、新しいコンポーネントをインポートしてルートコンポーネントにレンダリングする必要があります。 App.jsを開きます:
nano src/components/App/App.js
強調表示されたコードを追加して、コンポーネントをインポートしてレンダリングします。
async-tutorial / src / components / App / App.js
import React from 'react';
import './App.css';
import RiverInformation from '../RiverInformation/RiverInformation';
function App() {
return (
<div className="wrapper">
<h1>World's Longest Rivers</h1>
<RiverInformation />
</div>
);
}
export default App;
ファイルを保存して閉じます。
最後に、アプリを読みやすくするために、スタイルを追加します。 App.cssを開きます:
nano src/components/App/App.css
CSSを次のように置き換えて、wrapperクラスにパディングを追加します。
async-tutorial / src / components / App / App.css
.wrapper {
padding: 20px
}
ファイルを保存して閉じます。 これを行うと、ブラウザが更新され、基本コンポーネントがレンダリングされます。
このチュートリアルでは、データを返すための汎用サービスを作成します。 サービスとは、特定のタスクを実行するために再利用できるコードを指します。 コンポーネントは、サービスがその情報を取得する方法を知る必要はありません。 知っておく必要があるのは、サービスがPromiseを返すことだけです。 この場合、データ要求はsetTimeoutでシミュレートされ、データを提供する前に指定された時間待機します。
src/ディレクトリの下にservicesという名前の新しいディレクトリを作成します。
mkdir src/services
このディレクトリには、非同期機能が保持されます。 rivers.jsというファイルを開きます。
nano src/services/rivers.js
ファイル内で、promiseを返すgetRiverInformationという関数をエクスポートします。 promise内に、1500ミリ秒後にpromiseを解決するsetTimeout関数を追加します。 これにより、データが解決されるのを待っている間にコンポーネントがどのようにレンダリングされるかを確認する時間ができます。
async-tutorial / src / services / rivers.js
export function getRiverInformation() {
return new Promise((resolve) => {
setTimeout(() => {
resolve({
continent: 'Africa',
length: '6,650 km',
outflow: 'Mediterranean'
})
}, 1500)
})
}
このスニペットでは、河川情報をハードコーディングしていますが、この関数は、API呼び出しなど、使用する可能性のある非同期関数と同様です。 重要な部分は、コードがpromiseを返すことです。
ファイルを保存して閉じます。
データを返すサービスができたので、それをコンポーネントに追加する必要があります。 これにより、問題が発生する場合があります。 コンポーネント内で非同期関数を呼び出し、useStateフックを使用してデータを変数に設定するとします。 コードは次のようになります。
import React, { useState } from 'react';
import { getRiverInformation } from '../../services/rivers';
export default function RiverInformation() {
const [riverInformation, setRiverInformation] = useState({});
getRiverInformation()
.then(d => {
setRiverInformation(d)
})
return(
...
)
}
データを設定すると、フックを変更するとコンポーネントが再レンダリングされます。 コンポーネントが再レンダリングされると、getRiverInformation関数が再度実行され、コンポーネントが解決されると状態が設定され、別の再レンダリングがトリガーされます。 ループは永遠に続きます。
この問題を解決するために、ReactにはuseEffectと呼ばれる特別なフックがあり、特定のデータが変更されたときにのみ実行されます。
useEffect フックは、最初の引数として function を受け入れ、2番目の引数としてトリガーのarrayを受け入れます。 この関数は、レイアウトとペイントの後の最初のレンダリングで実行されます。 その後、トリガーの1つが変更された場合にのみ実行されます。 空の配列を指定すると、1回だけ実行されます。 トリガーの配列を含めない場合、レンダリングのたびに実行されます。
RiverInformation.jsを開きます:
nano src/components/RiverInformation/RiverInformation.js
useStateフックを使用して、riverInformationという変数とsetRiverInformationという関数を作成します。 非同期機能が解決したときにriverInformationを設定して、コンポーネントを更新します。 次に、getRiverInformation関数をuseEffectでラップします。 必ず2番目の引数として空の配列を渡してください。 約束が解決したら、riverInformationをsetRiverInformation関数で更新します。
async-tutorial / src / components / RiverInformation / RiverInformation.js
import React, { useEffect, useState } from 'react';
import { getRiverInformation } from '../../services/rivers';
export default function RiverInformation() {
const [riverInformation, setRiverInformation] = useState({});
useEffect(() => {
getRiverInformation()
.then(data =>
setRiverInformation(data)
);
}, [])
return(
<div>
<h2>River Information</h2>
<ul>
<li>Continent: {riverInformation.continent}</li>
<li>Length: {riverInformation.length}</li>
<li>Outflow: {riverInformation.outflow}</li>
</ul>
</div>
)
}
非同期関数が解決したら、順序付けされていないリストを新しい情報で更新します。
ファイルを保存して閉じます。 これを行うと、ブラウザが更新され、関数が解決された後にデータが表示されます。
データがロードされる前にコンポーネントがレンダリングされることに注意してください。 非同期コードの利点は、最初のレンダリングがブロックされないことです。 この場合、データなしでリストを表示するコンポーネントがありますが、スピナーまたはスケーラブルベクターグラフィック(SVG)プレースホルダーをレンダリングすることもできます。
ユーザー情報や変更されないリソースのリストを取得している場合など、データを1回だけロードする必要がある場合があります。 しかし、多くの場合、非同期関数にはいくつかの引数が必要になります。 そのような場合、データが変更されるたびにuseEffectフックの使用をトリガーする必要があります。
これをシミュレートするには、サービスにデータを追加します。 rivers.jsを開きます:
nano src/services/rivers.js
次に、さらにいくつかの河川のデータを含むオブジェクトを追加します。 name引数に基づいてデータを選択します。
async-tutorial / src / services / rivers.js
const rivers = {
nile: {
continent: 'Africa',
length: '6,650 km',
outflow: 'Mediterranean'
},
amazon: {
continent: 'South America',
length: '6,575 km',
outflow: 'Atlantic Ocean'
},
yangtze: {
continent: 'Asia',
length: '6,300 km',
outflow: 'East China Sea'
},
mississippi: {
continent: 'North America',
length: '6,275 km',
outflow: 'Gulf of Mexico'
}
}
export function getRiverInformation(name) {
return new Promise((resolve) => {
setTimeout(() => {
resolve(
rivers[name]
)
}, 1500)
})
}
ファイルを保存して閉じます。 次に、App.jsを開いて、さらにオプションを追加できるようにします。
nano src/components/App/App.js
App.js内に、 stateful 変数を作成し、useStateフックで選択した川を保持するように機能します。 次に、onClickハンドラーを使用して各河川にボタンを追加し、選択した河川を更新します。 nameと呼ばれるpropを使用して、riverをRiverInformationに渡します。
async-tutorial / src / components / App / App.js
import React, { useState } from 'react';
import './App.css';
import RiverInformation from '../RiverInformation/RiverInformation';
function App() {
const [river, setRiver] = useState('nile');
return (
<div className="wrapper">
<h1>World's Longest Rivers</h1>
<button onClick={() => setRiver('nile')}>Nile</button>
<button onClick={() => setRiver('amazon')}>Amazon</button>
<button onClick={() => setRiver('yangtze')}>Yangtze</button>
<button onClick={() => setRiver('mississippi')}>Mississippi</button>
<RiverInformation name={river} />
</div>
);
}
export default App;
ファイルを保存して閉じます。 次に、RiverInformation.jsを開きます。
nano src/components/RiverInformation/RiverInformation.js
nameを小道具として引き込み、getRiverInformation関数に渡します。 必ずnameをuseEffectのアレイに追加してください。そうしないと、再実行されません。
async-tutorial / src / components / RiverInformation / RiverInformation.js
import React, { useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import { getRiverInformation } from '../../services/rivers';
export default function RiverInformation({ name }) {
const [riverInformation, setRiverInformation] = useState({});
useEffect(() => {
getRiverInformation(name)
.then(data =>
setRiverInformation(data)
);
}, [name])
return(
<div>
<h2>River Information</h2>
<ul>
<li>Continent: {riverInformation.continent}</li>
<li>Length: {riverInformation.length}</li>
<li>Outflow: {riverInformation.outflow}</li>
</ul>
</div>
)
}
RiverInformation.propTypes = {
name: PropTypes.string.isRequired
}
このコードでは、PropTypesを使用した弱い型付けシステムも追加しました。これにより、小道具が文字列であることを確認できます。
ファイルを保存します。 これを行うと、ブラウザが更新され、さまざまな川を選択できます。 クリックしてからデータがレンダリングされるまでの遅延に注意してください。
useEffectアレイからname小道具を省略した場合、ブラウザコンソールでビルドエラーが発生します。 これは次のようになります。
ErrorCompiled with warnings. ./src/components/RiverInformation/RiverInformation.js Line 13:6: React Hook useEffect has a missing dependency: 'name'. Either include it or remove the dependency array react-hooks/exhaustive-deps Search for the keywords to learn more about each warning. To ignore, add // eslint-disable-next-line to the line before.
このエラーは、エフェクト内の関数に、明示的に設定していない依存関係があることを示しています。 この状況では、効果が機能しないことは明らかですが、propデータをコンポーネント内のステートフルデータと比較している場合があります。これにより、配列内のアイテムを追跡できなくなる可能性があります。
最後に行うことは、コンポーネントに防御プログラミングを追加することです。 これは、アプリケーションの高可用性を強調する設計原則です。 データが正しい形状でない場合や、APIリクエストからデータをまったく取得しない場合でも、コンポーネントが確実にレンダリングされるようにする必要があります。
アプリが現在のように、エフェクトはriverInformationを受信した任意のタイプのデータで更新します。 これは通常オブジェクトになりますが、そうでない場合は、オプションのチェーンを使用して、エラーがスローされないようにすることができます。
RiverInformation.js内で、オブジェクトドットチェーンのインスタンスをオプションのチェーンに置き換えます。 動作するかどうかをテストするには、デフォルトのオブジェクト{}をuseState関数から削除します。
async-tutorial / src / components / RiverInformation / RiverInformation.js
import React, { useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import { getRiverInformation } from '../../services/rivers';
export default function RiverInformation({ name }) {
const [riverInformation, setRiverInformation] = useState();
useEffect(() => {
getRiverInformation(name)
.then(data =>
setRiverInformation(data)
);
}, [name])
return(
<div>
<h2>River Information</h2>
<ul>
<li>Continent: {riverInformation?.continent}</li>
<li>Length: {riverInformation?.length}</li>
<li>Outflow: {riverInformation?.outflow}</li>
</ul>
</div>
)
}
RiverInformation.propTypes = {
name: PropTypes.string.isRequired
}
ファイルを保存して閉じます。 これを行うと、コードがオブジェクトではなくundefinedのプロパティを参照している場合でも、ファイルはロードされます。
通常、防御プログラミングはベストプラクティスと見なされますが、応答を保証できない場合のAPI呼び出しなどの非同期関数では特に重要です。
このステップでは、Reactで非同期関数を呼び出しました。 useEffectフックを使用して、再レンダリングをトリガーせずに情報をフェッチし、useEffect配列に条件を追加して新しい更新をトリガーしました。
次のステップでは、コンポーネントがマウントされたときにのみコンポーネントが更新されるように、アプリにいくつかの変更を加えます。 これは、アプリがメモリリークを回避するのに役立ちます。
ステップ2—マウントされていないコンポーネントでのエラーの防止
この手順では、マウントされていないコンポーネントのデータが更新されないようにします。 非同期プログラミングではデータがいつ解決されるかわからないため、コンポーネントが削除された後にデータが解決されるリスクは常にあります。 マウントされていないコンポーネントのデータを更新することは非効率的であり、アプリが必要以上のメモリを使用しているメモリリークを引き起こす可能性があります。
この手順を完了すると、useEffectフックにガードを追加して、コンポーネントがマウントされている場合にのみデータを更新することで、メモリリークを防ぐ方法がわかります。
現在のコンポーネントは常にマウントされるため、 DOM から削除された後、コードがコンポーネントを更新しようとする可能性はありませんが、ほとんどのコンポーネントはそれほど信頼性がありません。 これらは、ユーザーがアプリケーションを操作するときにページに追加され、ページから削除されます。 非同期関数が解決される前にコンポーネントがページから削除されると、メモリリークが発生する可能性があります。
問題をテストするには、App.jsを更新して、川の詳細を追加および削除できるようにします。
App.jsを開きます:
nano src/components/App/App.js
川の詳細を切り替えるボタンを追加します。 useReducerフックを使用して、詳細を切り替える関数と、切り替えられた状態を格納する変数を作成します。
async-tutorial / src / components / App / App.js
import React, { useReducer, useState } from 'react';
import './App.css';
import RiverInformation from '../RiverInformation/RiverInformation';
function App() {
const [river, setRiver] = useState('nile');
const [show, toggle] = useReducer(state => !state, true);
return (
<div className="wrapper">
<h1>World's Longest Rivers</h1>
<div><button onClick={toggle}>Toggle Details</button></div>
<button onClick={() => setRiver('nile')}>Nile</button>
<button onClick={() => setRiver('amazon')}>Amazon</button>
<button onClick={() => setRiver('yangtze')}>Yangtze</button>
<button onClick={() => setRiver('mississippi')}>Mississippi</button>
{show && <RiverInformation name={river} />}
</div>
);
}
export default App;
ファイルを保存します。 ブラウズを実行するとリロードされ、詳細を切り替えることができます。
川をクリックし、すぐに Toggle Details ボタンをクリックして、詳細を非表示にします。 Reactは、潜在的なメモリリークがあることを警告するエラーを生成します。
この問題を修正するには、useEffect内の非同期機能をキャンセルまたは無視する必要があります。 RxJS などのライブラリを使用している場合は、useEffectフックに関数を返すことで、コンポーネントがアンマウントされたときに非同期アクションをキャンセルできます。 それ以外の場合は、マウントされた状態を格納するための変数が必要になります。
RiverInformation.jsを開きます:
nano src/components/RiverInformation/RiverInformation.js
useEffect関数内で、mountedという変数を作成し、trueに設定します。 .thenコールバック内で、mountedがtrueの場合、条件を使用してデータを設定します。
async-tutorial / src / components / RiverInformation / RiverInformation.js
import React, { useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import { getRiverInformation } from '../../services/rivers';
export default function RiverInformation({ name }) {
const [riverInformation, setRiverInformation] = useState();
useEffect(() => {
let mounted = true;
getRiverInformation(name)
.then(data => {
if(mounted) {
setRiverInformation(data)
}
});
}, [name])
return(
<div>
<h2>River Information</h2>
<ul>
<li>Continent: {riverInformation?.continent}</li>
<li>Length: {riverInformation?.length}</li>
<li>Outflow: {riverInformation?.outflow}</li>
</ul>
</div>
)
}
RiverInformation.propTypes = {
name: PropTypes.string.isRequired
}
変数ができたので、コンポーネントがアンマウントされたときにそれを反転できるようにする必要があります。 useEffectフックを使用すると、コンポーネントがアンマウントされたときに実行される関数を返すことができます。 mountedをfalseに設定する関数を返します。
async-tutorial / src / components / RiverInformation / RiverInformation.js
import React, { useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import { getRiverInformation } from '../../services/rivers';
export default function RiverInformation({ name }) {
const [riverInformation, setRiverInformation] = useState();
useEffect(() => {
let mounted = true;
getRiverInformation(name)
.then(data => {
if(mounted) {
setRiverInformation(data)
}
});
return () => {
mounted = false;
}
}, [name])
return(
<div>
<h2>River Information</h2>
<ul>
<li>Continent: {riverInformation?.continent}</li>
<li>Length: {riverInformation?.length}</li>
<li>Outflow: {riverInformation?.outflow}</li>
</ul>
</div>
)
}
RiverInformation.propTypes = {
name: PropTypes.string.isRequired
}
ファイルを保存します。 そうすると、エラーなしで詳細を切り替えることができます。
アンマウントすると、コンポーネントuseEffectが変数を更新します。 非同期関数は引き続き解決されますが、マウントされていないコンポーネントには変更が加えられません。 これにより、メモリリークが防止されます。
この手順では、コンポーネントがマウントされている場合にのみアプリの更新状態を作成しました。 useEffectフックを更新して、コンポーネントがマウントされているかどうかを追跡し、コンポーネントがマウント解除されたときに値を更新する関数を返しました。
次のステップでは、コンポーネントを非同期的にロードして、コードを小さなバンドルに分割し、ユーザーが必要に応じてロードします。
ステップ3—Suspenseおよびlazyを使用したコンポーネントの遅延読み込み
このステップでは、コードをReactSuspenseとlazyで分割します。 アプリケーションが大きくなるにつれて、最終的なビルドのサイズも大きくなります。 ユーザーにアプリケーション全体のダウンロードを強制するのではなく、コードを小さなチャンクに分割することができます。 React Suspenseおよびlazyは、webpackやその他のビルドシステムと連携して、ユーザーがオンデマンドでロードできるようにコードを小さな部分に分割します。 将来的には、Suspenseを使用して、APIリクエストを含むさまざまなデータを読み込むことができるようになります。
この手順を完了すると、コンポーネントを非同期でロードして、大きなアプリケーションをより小さく、より焦点を絞ったチャンクに分割できるようになります。
これまでは、データを非同期的にロードすることだけを扱ってきましたが、コンポーネントを非同期的にロードすることもできます。 このプロセスは、コード分割と呼ばれることが多く、コードバンドルのサイズを縮小するのに役立ちます。これにより、ユーザーがアプリケーションの一部のみを使用している場合でも、アプリケーション全体をダウンロードする必要がなくなります。
ほとんどの場合、コードを静的にインポートしますが、ステートメントの代わりに関数としてimportを呼び出すことにより、コードを動的にインポートできます。 コードは次のようになります。
import('my-library')
.then(library => library.action())
Reactは、lazyおよびSuspenseと呼ばれる追加のツールセットを提供します。 React Suspenseは、最終的にデータロードの処理に拡張されますが、今のところ、これを使用してコンポーネントをロードできます。
App.jsを開きます:
nano src/components/App/App.js
次に、lazyとSuspenseをreactからインポートします。
async-tutorial / src / components / App / App.js
import React, { lazy, Suspense, useReducer, useState } from 'react';
import './App.css';
import RiverInformation from '../RiverInformation/RiverInformation';
function App() {
const [river, setRiver] = useState('nile');
const [show, toggle] = useReducer(state => !state, true);
return (
<div className="wrapper">
<h1>World's Longest Rivers</h1>
<div><button onClick={toggle}>Toggle Details</button></div>
<button onClick={() => setRiver('nile')}>Nile</button>
<button onClick={() => setRiver('amazon')}>Amazon</button>
<button onClick={() => setRiver('yangtze')}>Yangtze</button>
<button onClick={() => setRiver('mississippi')}>Mississippi</button>
{show && <RiverInformation name={river} />}
</div>
);
}
export default App;
lazyとSuspsenseには2つの異なるジョブがあります。 lazy関数を使用して、コンポーネントを動的にインポートし、変数に設定します。 Suspenseは、コードの読み込み中にフォールバックメッセージを表示するために使用する組み込みコンポーネントです。
import RiverInformation from '../RiverInformation/RiverInformation';をlazyの呼び出しに置き換えます。 結果をRiverInformationという変数に割り当てます。 次に、{show && <RiverInformation name={river} />}をSuspenseコンポーネントでラップし、<div>をLoading Componentのメッセージでfallback小道具にラップします。
async-tutorial / src / components / App / App.js
import React, { lazy, Suspense, useReducer, useState } from 'react';
import './App.css';
const RiverInformation = lazy(() => import('../RiverInformation/RiverInformation'));
function App() {
const [river, setRiver] = useState('nile');
const [show, toggle] = useReducer(state => !state, true);
return (
<div className="wrapper">
<h1>World's Longest Rivers</h1>
<div><button onClick={toggle}>Toggle Details</button></div>
<button onClick={() => setRiver('nile')}>Nile</button>
<button onClick={() => setRiver('amazon')}>Amazon</button>
<button onClick={() => setRiver('yangtze')}>Yangtze</button>
<button onClick={() => setRiver('mississippi')}>Mississippi</button>
<Suspense fallback={<div>Loading Component</div>}>
{show && <RiverInformation name={river} />}
</Suspense>
</div>
);
}
export default App;
ファイルを保存します。 その場合、ページをリロードすると、コンポーネントが動的にロードされていることがわかります。 読み込み中のメッセージを表示したい場合は、ChromeWebブラウザで応答をスロットルできます。
ChromeまたはFirefoxのネットワークタブに移動すると、コードがさまざまなチャンクに分割されていることがわかります。
各チャンクはデフォルトで番号を取得しますが、Create React Appをwebpackと組み合わせて使用すると、動的インポートによってコメントを追加することでチャンク名を設定できます。
App.jsで、import関数内に/* webpackChunkName: "RiverInformation" */のコメントを追加します。
async-tutorial / src / components / App / App.js
import React, { lazy, Suspense, useReducer, useState } from 'react';
import './App.css';
const RiverInformation = lazy(() => import(/* webpackChunkName: "RiverInformation" */ '../RiverInformation/RiverInformation'));
function App() {
const [river, setRiver] = useState('nile');
const [show, toggle] = useReducer(state => !state, true);
return (
<div className="wrapper">
<h1>World's Longest Rivers</h1>
<div><button onClick={toggle}>Toggle Details</button></div>
<button onClick={() => setRiver('nile')}>Nile</button>
<button onClick={() => setRiver('amazon')}>Amazon</button>
<button onClick={() => setRiver('yangtze')}>Yangtze</button>
<button onClick={() => setRiver('mississippi')}>Mississippi</button>
<Suspense fallback={<div>Loading Component</div>}>
{show && <RiverInformation name={river} />}
</Suspense>
</div>
);
}
export default App;
ファイルを保存して閉じます。 これを行うと、ブラウザが更新され、RiverInformationチャンクに一意の名前が付けられます。
このステップでは、コンポーネントを非同期でロードします。 lazyおよびSuspenseを使用して、コンポーネントを動的にインポートし、コンポーネントのロード中にロードメッセージを表示しました。 また、読みやすさとデバッグを向上させるために、webpackチャンクにカスタム名を付けました。
結論
非同期機能は、効率的なユーザーフレンドリーなアプリケーションを作成します。 ただし、それらの利点には、プログラムのバグに発展する可能性のある微妙なコストが伴います。 これで、ユーザーに表示可能なアプリケーションを提供しながら、大きなアプリケーションを小さな部分に分割し、非同期データをロードできるツールができました。 この知識を使用して、APIリクエストと非同期データ操作をアプリケーションに組み込み、高速で信頼性の高いユーザーエクスペリエンスを作成できます。
Reactチュートリアルをもっと読みたい場合は、 Reactトピックページを確認するか、React.jsシリーズのコーディング方法ページに戻ってください。