網站搜尋

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。