React v16.6 から利用可能になったReact.lazy
ならびにSuspense
を早速使用してみました。
この機能の嬉しいところは非同期処理時におけるコンポーネントの扱いが非常に楽になるということです。
レガシーな実装方法
この機能が追加される以前は非同期で処理を行う際にローダーを表示して処理が終了したら結果を画面に表示するといった実装する時に、以下のような方法を採用していました。
- 処理中かどうか判断するステート
isExecuting
を用意する。 - 処理開始時に
isExecuting
をtrueにする -
isExecuting
がtrueの時に「Please wait...」というテキストを表示 - 処理完了時に結果を表示
つまり、ステートが変更されたらコンポーネントをレンダリングするという、Reactの仕組みに則って実現させるという方法になります。
コードに起こすと以下のような感じです。以下の例ではsetTimeoutで5秒間待っている間は「Please wait...」と表示して、完了したら「Done!」という文言を表示するという単純な例です。
import React, { Component } from 'react';
import MyComponent from './MyComponent';
class App extends Component {
render() {
return (
<div className="App">
<header className="App-header">
<MyComponent/>
</header>
</div>
);
}
}
export default App;
import React, { Component } from 'react';
class MyComponent extends Component {
constructor(props) {
super(props);
this.state = {
isExecuting: false,
result: ''
}
}
componentDidMount() {
this.timeout(5000);
}
render() {
return (
<div>{this.state.result}</div>
)
}
timeout(msec) {
this.setState({isExecuting: true, result: 'Please wait...'});
setTimeout(() => {
this.setState({isExecuting: false, result: 'Done'});
}, msec);
}
}
export default MyComponent;
React.lazyおよびReact.Suspenseを使った実装方法
React.lazy()
ざっくりいうと遅延ロード用のコンポーネントをimportするための機能です。この方法で読み込んだコンポーネントをReact.Suspense
を使ってfallbackの設定などができます。
const LazyComponent = lazy(() => import('./LazyComponent'));
React.Suspense
React.Suspenseは遅延ロード用のコンポーネントをラップするコンポーネントで、このコンポーネントを使ってfallbackの設定をすることができます。
<Suspense fallback={<div>ロード完了まで表示させたい内容</div>}>
<LazyComponent>
</Suspense>
全体のコード
以下が通しで実装したコードです。
import React, { Component, lazy, Suspense } from 'react';
// 遅延ロードしたいコンポーネントを呼び出す
const LazyComponent = lazy(() => import('./LazyComponent'));
class App extends Component {
render() {
return (
<div className="App">
<header className="App-header">
<Suspense fallback={<div>Please wait...</div>}>
<LazyComponent/>
</Suspense>
</header>
</div>
);
}
}
export default App;
import React, { Component } from 'react';
let result = null;
const timeout = (msec) => new Promise(resolve => {
setTimeout(resolve, msec)
});
const LazyComponent = () => {
if (result !== null) {
return (
<div>{result}</div>
)
}
throw new Promise(async(resolve) => {
await timeout(1000);
result = 'Done'
resolve();
})
};
export default LazyComponent;
簡単なコードなので実感が湧きにくいですが、ステートを使わない分かなりスマートに記述することが可能になります。
ちなみに、これらの機能はサーバーサイドレンダリングでは使えないようで、今後のアップデートで実装されるのかもしれませんが、公式ドキュメントではサーバーサイドで使う場合にはLoadable Componentというものが推奨されています。