最近,React宣布了React生态系统的一个功能- 并发模式 。 这将使我们能够在所需的时间内停止或延迟组件的执行。 它将帮助React应用程序保持响应速度,并优雅地适应用户的设备功能和网络速度。
并发模式包含一组新功能,其中最大的功能之一就是Suspense和一种新的数据获取方法。
基本上,有三种方法可以做到:
- 渲染时获取:我们开始渲染组件,并且每个组件都可能触发其效果和生命周期方法中的数据获取。 这方面的一个很好的例子是
fetch
在useEffect
。 - 提取然后呈现:尽快开始获取下一个屏幕的所有数据。 数据准备好后,渲染新屏幕。 数据到达之前我们什么也做不了。 这样的例子是拥有一个
Container
组件,该组件处理数据获取并在我们收到所有信息后有条件地呈现子表示组件。 - 渲染-随你取:开始获取全部为下一个画面所需要的数据尽早,并立即开始呈现新的屏幕,即使我们拿到了入网响应 。 随着数据流的进入,React重试渲染仍需要数据的组件,直到它们准备就绪为止。
我相信前两种方法的概念是众所周知的,并且肯定会在您的代码中提出。 让我们直接了解按需渲染 方法。
按需渲染
您可能已经注意到,这种方法的解释包括两个部分:
- 尽早开始加载数据。
- 开始尝试渲染可能仍需要数据的组件。
早取
让我们一起构建一个加载主要股票指数的应用程序。 为此,我们有一个简单的“加载”按钮。 单击它后,我们将立即开始加载数据:
const App = () => {
const [prefetchedIndexes, setPrefetchedIndexes] = useState();
return (
<>
<button
onClick={() => {
setPrefetchedIndexes(prefetchQuery(`${API}/majors-indexes`));
}}
>
Load all indexes
</button>
{prefetchedIndexes && (
<IndexList prefetchedIndexes={prefetchedIndexes} />
)}
</>
);
};
prefetchQuery
是执行fetch
请求并返回要传递给<IndexList />
组件的对象的函数。 此示例的关键之处在于,我们是从onClick
事件而不是在渲染阶段触发获取的。
提前悬念渲染
上面示例的第二部分是我们将对象从prefetchQuery
保存到状态,并立即开始呈现<IndexList />
。
另一方面,我们也不想用空数据渲染列表,因此理想情况下,我们希望能够暂停渲染,直到我们拥有所有数据而不编写if (isLoading) return null
。
Suspense是一种机制,用于使数据获取库与React进行通信,以告知组件正在读取的数据尚未准备好 。
然后,React可以等待它准备就绪并更新UI。
让我给你看一个例子:
const IndexList = ( { prefetchedIndexes } ) => {
const data = usePrefetchedQuery(prefetchedIndexes);
return data.majorIndexesList.map( index => (
< div key = {index.ticker} >
Show {index.ticker}
</ div >
));
};
const App = () => {
const [prefetchedIndexes, setPrefetchedIndexes] = useState();
return (
<>
<button
onClick={() => {
setPrefetchedIndexes(prefetchQuery(`${API}/majors-indexes`));
}}
>
Load all indexes
</button>
{prefetchedIndexes && (
<Suspense fallback={<span>Loading indexes list...</span>}>
<IndexList prefetchedIndexes={prefetchedIndexes} />
</Suspense>
)}
</>
);
};
要利用Suspense,您只需要用它包装您的组件。 它接受一个fallback
支持:等待数据时要显示的元素。
如何与暂停同步获取数据?
既然您已经了解了暂挂和预取实践,那么您想知道这是如何协同工作的。 所以,这是这个难题的最后一步。 为了解决这个问题,让我们最后检查一下prefetchQuery
函数。
function wrapPromise ( promise ) {
let status = "pending" ;
let result;
let suspender = promise.then(
r => {
status = "success" ;
result = r;
},
e => {
status = "error" ;
result = e;
}
);
return {
read() {
if (status === "pending" ) {
throw suspender;
} else if (status === "error" ) {
throw result;
} else if (status === "success" ) {
return result;
}
}
};
}
// Function that reads resource object
// It could also include Cache logic
export const usePrefetchedQuery = prefetchedQuery => prefetchedQuery.read();
export const prefetchQuery = ( input, init ) => {
// Make fetch request
const promise = fetch(input, init).then( response => response.json());
// Return resource for Suspense
return wrapPromise(promise);
};
不要担心它的复杂性,它实际上很简单。
首先,我们获取一个URL并将其传递给本机fetch
wrapPromise
函数,接收一个wrapPromise
,然后将其传递给wrapPromise
函数。
此函数使用read()
方法返回一个对象:
- 如果仍未兑现承诺,我们将兑现该承诺。
- 如果一个承诺被错误解决,我们就会抛出错误。
- 如果诺言得以解决,则只需返回数据即可。
实际上,与传统的获取操作相比,我们唯一的区别是即将实现的承诺。
当你有usePrefetchedQuery
在IndexList
,它只是执行read()
方法。 如果还没有数据,它会在实际呈现任何内容之前抛出一个承诺, Suspense
会抓住它。
如何尝试呢?
React团队引入了 releases branch with a modern API. 的
experimental
releases branch with a modern API.
为此,您需要运行npm i react@experimental react-dom@experimental
并在本地使用它。 我还为您在CodeSandbox上创建了一个实时示例,该示例显示了我在一个工作项目中共同完成的所有工作。
我可以在我的生产项目中使用它吗?
否。并发模式仍在开发中,某些实现细节可能会更改。 使用实验版本熟悉新概念,并提出自己的想法。
例如,如何在路由器中集成预取实践或提供缓存数据的好方法。
更多资源
- 具有悬念的中继的实验版 。
- 很快,您就可以将预加载功能连接到路由器。 看看https://github.com/ReactTraining/react-router/pull/7010或Navi路由器 。
- 并发模式简介 。
- 暂挂简介进行数据提取 。
- 带有示例的实时沙箱 。
From: https://hackernoon.com/implementing-the-prefetch-pattern-in-react-concurrent-mode-h4r3twc