如何在 TypeScript 中使用 React Suspense

3
我没有找到任何文件或示例来解释这个问题,但从我的理解来说,要使用suspense,您必须向父组件添加Suspense边界,并从子组件中删除async/await并进行懒加载。但是,当移除async/await时,它会破坏类型,因为所有内容都是一个promise。
例如,将await (await fetch('some-api')).json();更改为fetch('some-api').json() 会导致错误 .json不是一个函数
值也无法被输入类型,因为const data: MyData = fetchData()会出错,因为fetchData返回的是 Promise<MyData>
有了Suspense。
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>
  );
}

在使用Suspense之前

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>
  );
}

使用 suspense 和 lazy loading 应该用于代码分割,而不是数据获取。如果您想以这种方式使用它,您的代码需要在获取完成之前抛出错误,并在完成后重置自身。但是,不建议在 SSR 框架之外(如 Next.js 和 Relay 等)使用它来获取数据。 - super
数据获取的暂停功能是实验性的。请在此处查看更多信息:https://17.reactjs.org/docs/concurrent-mode-suspense.html - Tasos
好的,谢谢。我正在使用Next.js,但仍然不知道如何添加类型。 - Lance
@Tasos 还有 https://reactjs.org/docs/react-api.html#reactsuspense - Lance
1
是的,@Lance,但正如描述中所说,它还没有准备好进行数据获取。 - Tasos
1个回答

1
TL;DR - fetchData的类型签名需要是Data,而不是Promise(因为它实际上是fetchData(): Data with effect ReactSuspense或fetchData(): Data throws Promise
详细信息:
Suspense不会阻塞异步代码,它将异步隐藏在间接性后面。实际上,它试图将抢占式调度引入到协作调度环境中(其中运行时不会停止您的代码,直到您的代码达到了一个您明确注释为“我让出地板”的点)。
大致工作原理:
- 运行组件 - 在Child内部调用fetchData - fetchData必须以某种方式向React“发出信号”,即“嘿,我需要你打断Child,因为我们需要等待某些条件才能恢复Child”。 - throw和catch是JavaScript提供的唯一信号机制 - 所以fetchData抛出一个promise,React的运行时捕获它。这意味着Child停止执行(被抢占)。React向fetchData的promise添加then监听器,该监听器将稍后将Child放回要“渲染”的项目队列中。 - 后来,该promise得到了满足。React重新呈现Child。fetchData需要缓存promise的结果,以便下次使用相同的参数调用它时,它将同步返回缓存的结果。 - 这意味着fetchData的类型不是(如您所期望的)Promise,而是仅为Data。

好的,明白了,谢谢。请问在缓存大量数据的最佳方式方面有什么建议吗? - Lance
1
通常情况下,只需在Map <SomeKey,SomeResult>上关闭 - 对于fetchById,它是Map <Id,Data> - 对于更广泛的fetch,它将是某种规范化的Request(因此fetch({x:1,y:2})fetch({y:2,x:1})都可以找到相同的Data,即使键不是按相同的顺序排列的,例如 - 具体取决于fetch所访问的服务的语义。 - Sean Vieira

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接