在用 React 处理业务的过程中,经常会遇到这样的场景:某一个 UI 需要等待网络请求来展示,在等待的过程中,需要显示 Loading 界面,并在请求完成后,显示真正的 UI。这种情况,和按需加载模块的行为非常类似。既然 React.Suspense + React.lazy 可以组合用于按需加载模块时候的 UI 展示,那么是否可以使用同样的组合来完成类似等待网络请求的 UI 显示呢?答案是肯定的。下面给出一个示例代码:
function sleep(time) {
return new Promise(resolve => setTimeout(resolve, time));
}
const fakeFetch = () => sleep(1000).then(() => "finished!");
const Component = React.lazy(() =>
fakeFetch().then(text => ({
default: () => <div>{text}</div>
}))
);
const App = () => (
<div className="App">
<React.Suspense fallback={<div>loading...</div>}>
<h1>Hello World</h1>
<Component />
</React.Suspense>
</div>
);
如此一来,在 Promise 没有返回的时候,组件会显示 <div>loading...</div>
。而等到 Promise resolve 之后,就会显示真正的 UI。
几点说明:
React.lazy
本身是为import()
设计的,所以在 Promise 返回的时候,需要将组件放到default
属性下面,保持和import()
的行为一致;React.Suspense
和React.lazy
的组合,本质上内部是使用了throw
+componentDidCatch
的方式进行实现的,因而如果不使用React.lazy
,直接在组件内throw Promise
,也可以达到类似的效果:
const fakeFetch = fn => sleep(1000).then(() => fn("finished!"));
let data = "before";
const Component = () => {
if (data === "before") {
throw fakeFetch(newData => {
data = newData;
});
}
return <div>{data}</div>;
};
const App = () => (
<div className="App">
<React.Suspense fallback={<div>loading...</div>}>
<h1>Hello World</h1>
<Component />
</React.Suspense>
</div>
);