如何将初始远程API数据获取逻辑与React.Suspense整合?
实现方案
你不需要引入Relay之类的第三方库,有两种纯原生的轻量方案可以实现需求:
方案1:自定义Suspense兼容的资源加载器(更灵活)
Suspense的核心逻辑是:只要子组件渲染时抛出未完成的Promise,就会继续展示fallback,直到所有抛出的Promise都被resolve。我们可以基于这个特性自己实现元数据的加载逻辑,和懒加载组件的Promise并行等待。
步骤1:实现通用资源加载工具
// 符合Suspense规范的资源加载工具 function createResource(fetcher) { let status = 'pending' let result // 立即执行请求 const promise = fetcher().then( (data) => { status = 'success' result = data }, (err) => { status = 'error' result = err } ) return { read() { if (status === 'pending') throw promise if (status === 'error') throw result return result } } }
步骤2:初始化元数据资源+封装数据注入组件
import { createContext } from 'react' // 应用初始化时立即发起元数据请求 const appMetaDataResource = createResource(getAppMetaData) // 可以配合Context把元数据透传给所有子组件,不用重复请求 export const AppMetaDataContext = createContext(null) function AppMetaDataLoader({ children }) { // 调用read方法,pending时会抛出Promise被Suspense捕获 const metaData = appMetaDataResource.read() return ( <AppMetaDataContext.Provider value={metaData}> {children} </AppMetaDataContext.Provider> ) }
步骤3:修改路由结构
只需要在原Suspense内部套一层刚才写的加载器即可,不需要其他改动:
<ErrorBoundary> <Suspense fallback={<div className={styles.loader}><Loader /></div>}> {/* 新增这一层,Suspense会同时等待元数据和所有懒加载组件加载完成 */} <AppMetaDataLoader> <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> </AppMetaDataLoader> </Suspense> </ErrorBoundary>
方案2:包装懒加载逻辑(改动最小)
如果你不想接触Suspense的throw Promise逻辑,还有更简单的方法:把元数据请求的Promise和懒加载组件的导入Promise用Promise.all合并,React.lazy会等所有Promise完成后再渲染组件。
代码实现
// 顶层提前发起元数据请求,缓存Promise const appMetaDataPromise = getAppMetaData().then(data => { // 这里可以把元数据存到全局状态、localStorage或者你用的状态管理工具里 window.__APP_META_DATA__ = data return data }) // 封装自定义懒加载方法,自动等待元数据加载完成 function lazyWithMeta(importFn) { return React.lazy(() => Promise.all([importFn(), appMetaDataPromise]) .then(([moduleExports]) => moduleExports) ) } // 把你原来所有的React.lazy定义替换成这个方法即可 const Home = lazyWithMeta(() => import('./pages/Home')) const Lesson = lazyWithMeta(() => import('./pages/Lesson')) const Login = lazyWithMeta(() => import('./pages/Login')) const About = lazyWithMeta(() => import('./pages/About')) const NotFound = lazyWithMeta(() => import('./pages/NotFound'))
注意事项
两种方案的请求报错都会被外层的ErrorBoundary捕获,你可以在ErrorBoundary里统一处理元数据请求失败的场景,比如展示重试按钮。
内容的提问来源于stack exchange,提问作者i.brod
相关产品推荐
相关产品推荐

