如何将从远程 API 获取的初始数据与 React.Suspense 集成?

Posted

技术标签:

【中文标题】如何将从远程 API 获取的初始数据与 React.Suspense 集成?【英文标题】:How to integrate initial data fetching from a remote API, with React.Suspense? 【发布时间】:2021-11-09 21:21:39 【问题描述】:

我正在现有样板之上构建一个新的 React 应用程序。它使用延迟加载,结合 React.Suspense。

问题是,就像在大多数 React 应用程序中一样,我需要在每次应用程序加载时从服务器获取一些初始元数据。我们称之为“getAppMetaData”。

那么问题是什么?问题是,虽然 getAppMetaData 处于待处理状态,但我需要提供一些加载器/微调器。这正是 React.Suspense 所做的:它显示了“后备”用户界面。当然,我可以运行一个单独的加载器(实际上可以与回退 UI 相同),但这会产生 UX 问题,加载器的动画在程序之间“重新启动”。

那么,问题是,我如何将其他异步操作“集成”到此暂停中?简而言之:“我的后备 UI 已经显示,而块(来自延迟加载)已加载 - 那么我如何让它也等待 getAppMetaData?”

这是我的路由器:

<ErrorBoundary>
     <Suspense fallback=<div className=styles.loader><Loader /></div>>
        <Switch>
          <ProtectedRoute exact component=Home path="/">                    
          </ProtectedRoute>    
             <Route path="/lesson">
               <Lesson></Lesson>
             </Route>    
            <Route exact path="/login">
              <Login />
            </Route>
               <Route path="/about">
            <About />
            </Route>
            <Route path="*">
              <NotFound />
          </Route>
        </Switch>
      </Suspense>
    </ErrorBoundary>

React 文档指出,应该为此使用 Relay 库,但我不想为我的 API 调用使用任何特殊库,只是为了克服这个简单问题。它还指出:

如果我不使用 Relay 会怎样?如果你今天不使用 Relay,你可能 必须等待才能真正在应用中尝试 Suspense。迄今为止, 这是我们在生产中测试的唯一实现,并且是 有信心。

我所需要的只是将一个小的初始 API 调用集成到这个过程中。如何做呢? 任何建议将不胜感激。

【问题讨论】:

Suspense 只是一个组件,它捕获一个 Promise 并显示一个回退直到 Promise 被解决,所以你使用 Promise 加载数据并抛出它,这样 Suspense 组件就可以捕获它并显示数据加载时微调器 我明白了,但是我到底应该把这段代码放在哪里呢?我如何确保它被悬念抓住了?如果我有一些组件位于 Suspense 树中,我该如何实际操作呢?如果我从 useEffect 抛出一个承诺,我会得到一个未捕获的错误 【参考方案1】:

我会将 Suspense 的子组件移动到一个新组件中并从该组件中读取数据。

数据将使用自定义函数fetchData 加载,该函数将创建promise 并返回一个包含read 方法的对象,如果数据未准备好,该方法将抛出promise。

function fetchData() 
  let status = 'pending';
  let result;

  const promise = fetch('./data.json')
    .then(data => data.json())
    .then(r => 
      status = 'success';
      result = r;
    )
    .catch(e => 
      status = 'error';
      result = e;
    );

  return 
    read() 
      if (status === 'pending') 
        throw promise;
       else if (status === 'error') 
        throw result;
       else if (status === 'success') 
        return result;
      
    
  ;


const dataWrapper = fetchData();

function AppBody() 
  const data = dataWrapper.read();

  // you can now manipulate the data
  return (
    <Switch>
      <ProtectedRoute exact component=Home path="/"/>                    
      <Route path="/lesson" component=Lesson />
      <Route exact path="/login" component=Login />
      <Route path="/about" component=About />
      <Route path="*" component=NotFound />
    </Switch>
  )


function App() 
  return (
    <ErrorBoundary>
     <Suspense fallback=<div className=styles.loader><Loader /></div>>
       <AppBody/>
     </Suspense>
    </ErrorBoundary>
  );

这是stackblitz example

这段代码的灵感来自react documentationcodesandbox

【讨论】:

哇,这很好用。我一定会学习这段代码并理解其中的机制。谢谢。 您只需要了解Suspense 组件的工作是捕获promise 并显示一个备用组件,直到promise 被解决。 你将如何从这个创建一个自定义的钩子,它可以在任何组件中使用?(都在同一个 Suspense 树下)。可以让我从组件中调用“useSuspense(somePromise)”的东西。 最简单的方法是在自定义钩子中使用useMemo[] 来创建promise 并使用read 方法返回对象。更复杂的方法是结合useState(用于存储承诺)和useMemo 使用 read 方法构建对象,使用此方法您可以通过使用新承诺更新状态来刷新数据。 谢谢。你能解释一下我到底需要记住什么吗?

以上是关于如何将从远程 API 获取的初始数据与 React.Suspense 集成?的主要内容,如果未能解决你的问题,请参考以下文章