reactjs - 如何在 TypeScript 中使用 React Suspense
我還沒有找到任何文檔或範例來解釋這個,但據我所知,要使用 suspense,你必須在父組件中添加一個 Suspense 邊界,並從子組件中移除 async/await,然後以 lazy 的方式加載它。但當移除 async/await 時,類型就會出錯,因為所有東西都是一個 promise。
例如,將 await (await fetch('some-api')).json(); 改成 fetch('some-api').json() 會出現錯誤 .json is not a function。
值也無法被指定類型,因為 const data: MyData=fetchData() 會出錯,因為 fetchData 返回的是 Promise<MyData>。
充滿懸念
function Parent() {
return (
<Suspense fallback={<Spinner />}>
<Child />
</Suspense>
);
}
function Child() {
const data: MyData[] = fetchData(); //Error: Type 'Promise<MyData[]>' is missing the following properties from type 'MyData[]': length, pop, push, concat, and 29 more
return (
<div>{data.map(d => <p>d.name</p>)}</data>
);
}
懸疑之前
function Child() {
const [data, setData] = useState<MyData[]>([]);
useEffect(() => {
async function loadData() {
setData(await fetchData());
}
loadData();
}, []);
if (!data.length) return <Spinner />;
return (
<div>{data.map(d => <p>d.name</p>)}</data>
);
}
答案#1
簡述 - fetchData 的類型簽名只需要是 Data,而不是 Promise<Data>(因為它實際上是 fetchData(): Data with effect ReactSuspense 或 fetchData(): Data throws Promise<Data>)
詳細內容
Suspense 並不會阻塞非同步程式碼,它將非同步隱藏在間接層之後。實際上,它試圖在一個合作式排程環境中引入搶先式排程(在這種環境中,執行時可以在任何時候停止你的程式碼並切換到執行其他程式碼),而在合作式排程環境中,執行時不會停止你的程式碼,除非你的程式碼到達你明確標註「我放棄控制權」的點。
大致上的運作方式如下:
- 運行你的組件
<Child /> - 在
Child裡,你呼叫了fetchData fetchData必須以某種方式 向 React 發出訊號:「嘿,我需要你中斷Child,因為我們需要在某個條件滿足之前暫停Child的執行。」throw和catch是 JavaScript 提供的唯一信號機制
- 所以
fetchData拋出 一個 promise,而 React 的運行時 捕捉 它。這表示Child停止執行(它被搶先中止)。React 為fetchData的 promise 添加了一個then監聽器,該監聽器會將Child放回稍後要「渲染」的項目隊列中。 - 後來,該承諾得以實現。React 重新渲染了
Child。fetchData需要將承諾的結果 快取 起來,以便下次使用相同的參數呼叫時,它會 同步地 返回 快取的 結果。 - 這意味著
fetchData的類型 不是(如你所預期的)Promise<Data>,而是 僅僅Data。