Preact Query 状态恢复信号机制:IsRestoringProvider 与 useIsRestoring 全解析
【免费下载链接】query🤖 Powerful asynchronous state management, server-state utilities and data fetching for the web. TS/JS, React Query, Solid Query, Svelte Query and Vue Query.项目地址: https://gitcode.com/GitHub_Trending/qu/query
导读
在 TanStack Query 的 Preact 适配层(@tanstack/preact-query)中,IsRestoringProvider是一个承载"持久化查询客户端是否正在恢复"这一布尔信号的 Context Provider。它与配套的 useIsRestoring Hook 一起,被PersistQueryClientProvider用来向整棵组件树广播恢复状态,并被useQuery及其衍生 Hook 内部消费,以避免"持久化数据恢复"与"查询挂载"之间的竞态条件(race condition)。读完本文,你将掌握该 Provider 的定义方式、在持久化插件中的驱动逻辑,以及它如何在底层影响查询订阅与乐观结果计算。
一、IsRestoringProvider是什么
从 IsRestoringProvider.ts 的源码可以看到,它的类型签名极简:
const IsRestoringProvider: Provider<boolean> = IsRestoringContext.Provider;它是 Preact 内置createContext所创建上下文的.Provider。定义位于 IsRestoringProvider.ts:19,完整定义代码如下:
import { createContext } from 'preact' import { useContext } from 'preact/hooks' const IsRestoringContext = createContext(false) export const useIsRestoring = () => useContext(IsRestoringContext) export const IsRestoringProvider = IsRestoringContext.Provider从源码结构看,这是一个布尔型 Context:默认值为false,即"当前没有正在执行的恢复流程"。Provider 的职责非常单一——向其子树提供"持久化客户端当前是否正在被恢复"的布尔值;真正的读取方是配套导出的useIsRestoringHook。
二、谁在驱动它:PersistQueryClientProvider的恢复流程
IsRestoringProvider本身不包含任何恢复逻辑,它是被持久化查询客户端插件消费的信号载体。在 Preact 生态中,真正的驱动者是 PersistQueryClientProvider.tsx。
关键实现如下:
const [isRestoring, setIsRestoring] = useState(true) useEffect(() => { const options = { ...refs.current.persistOptions, queryClient: props.client, } if (!didRestore.current) { didRestore.current = true persistQueryClientRestore(options) .then(() => refs.current.onSuccess?.()) .catch(() => refs.current.onError?.()) .finally(() => { setIsRestoring(false) }) } return isRestoring ? undefined : persistQueryClientSubscribe(options) }, [props.client, isRestoring]) return ( <QueryClientProvider {...props}> <IsRestoringProvider value={isRestoring}>{children}</IsRestoringProvider> </QueryClientProvider> )这段代码揭示了完整的信号时序:
- 组件挂载时,
isRestoring初始为true; - 首次副作用触发
persistQueryClientRestore(options),从 storage 中异步恢复被持久化的查询缓存; - 在
finally中把isRestoring置回false,即恢复结束; - 只有恢复结束后,才会调用
persistQueryClientSubscribe(options)订阅后续缓存变更(以便后续把新查询继续写入持久化存储); IsRestoringProvider用value={isRestoring}将这个状态包裹在整个QueryClientProvider子树之外。
注意组件树结构:IsRestoringProvider包裹在QueryClientProvider内部、children之外,因此任何查询组件在读取 QueryClient 的同时,都能感知恢复状态。PersistQueryClientProvider同时接受onSuccess/onError回调,分别对应恢复成功与失败,其中onSuccess的异步结果会在persistQueryClientRestoreresolve 后被等待。
三、配套读取 Hook:useIsRestoring
要在自己的组件里感知恢复状态,应当使用同文件导出的 Hook:
export const useIsRestoring = () => useContext(IsRestoringContext)官方参考文档 useIsRestoring 给出的类型签名为:
function useIsRestoring(): boolean;返回值语义:
true:持久化的查询客户端正在被恢复(restore in progress);false:没有任何恢复流程在执行,即默认状态。
两个导出物(IsRestoringProvider与useIsRestoring)都在 preact-query/src/index.ts:56 从包的公共入口统一导出:
export { useIsRestoring, IsRestoringProvider } from './IsRestoringProvider'因此在应用中可以直接import { IsRestoringProvider, useIsRestoring } from '@tanstack/preact-query'。
四、内部消费:useQuery如何用它规避竞态
useIsRestoring不只是给开发者用——useQuery、useInfiniteQuery、useQueries等数据获取 Hook 自身也会读取它。以 useBaseQuery.ts:51 为例,所有查询 Hook 的公共底座会执行:
const isRestoring = useIsRestoring()随后它在下游产生了两个关键影响:
1. 控制乐观结果模式
defaultedOptions._optimisticResults = isRestoring ? 'isRestoring' : 'optimistic'当正在恢复时,_optimisticResults被置为'isRestoring',查询的"乐观结果"(在订阅外部存储前对QueryObserver结果做出的临时估算)会进入恢复专用模式,避免把尚未就绪的中间状态当作最终结果渲染。
2. 抑制订阅,直到恢复完成
const shouldSubscribe = !isRestoring && options.subscribed !== false useSyncExternalStore( useCallback( (onStoreChange) => { const unsubscribe = shouldSubscribe ? observer.subscribe(notifyManager.batchCalls(onStoreChange)) : noop ...恢复期间shouldSubscribe为false,观察者不订阅缓存变更(退化为noop);恢复结束后才真正开始订阅。这一机制的意义在于:如果查询在恢复完成前就抢先发起 fetch 并写回缓存,很可能覆盖掉刚从 storage 恢复出来的旧数据,从而造成持久化失效或闪烁。恢复期"静默挂起、恢复后订阅"的设计正是对这种竞态的兜底。
同样的模式也出现在多查询场景:useQueries(其实现同样位于 useQueries.ts)内部会批量读取useIsRestoring()的状态来决定是否订阅与如何构造乐观结果,并在tests/useQueries.test.tsx 与tests/useQuery.test.tsx 中有对应的测试覆盖;PersistQueryClientProvider的集成行为则体现在 PersistQueryClientProvider.test.tsx。
五、开发者侧的应用方式
结合上述源码,在实际 Preact 应用中的典型组合用法如下:
import { QueryClient } from '@tanstack/preact-query' import { PersistQueryClientProvider } from '@tanstack/preact-query-persist-client' import { createSyncStoragePersister } from '@tanstack/query-sync-storage-persister' const queryClient = new QueryClient() const persister = createSyncStoragePersister({ storage: window.localStorage, }) export function App() { return ( <PersistQueryClientProvider client={queryClient} persistOptions={{ persister }} onSuccess={() => console.log('restore finished')} > <Main /> </PersistQueryClientProvider> ) }若需要在恢复完成前展示"占位"内容(例如避免离线应用先渲染空态再突然跳出缓存数据),可以直接读取信号:
import { useIsRestoring } from '@tanstack/preact-query' function Main() { const isRestoring = useIsRestoring() if (isRestoring) { return <div>正在恢复已保存的数据…</div> } return <TodoList /> }这里useIsRestoring()读取的正是IsRestoringProvider提供的 Context 值:值为true期间挂起渲染,直到恢复完成(值回到false)后再渲染依赖缓存的 UI。由于恢复期间查询 Hook 本身不会发起多余的 fetch,这一方案既能避免闪屏,也不会产生与恢复流程竞争的请求。
六、小结
IsRestoringProvider是 IsRestoringProvider.ts 中导出的布尔型 Context Provider,类型为Provider<boolean>,默认值为false;- 它由 PersistQueryClientProvider.tsx 驱动:恢复开始时置
true,persistQueryClientRestore结束后在finally中置回false; - 配套的
useIsRestoringHook(见 useIsRestoring)供开发者判断恢复状态; - 底层
useBaseQuery(所有查询 Hook 的底座)会自动读取该状态,在恢复期以'isRestoring'模式计算乐观结果并抑制订阅,从框架层规避恢复与挂载查询的竞态。
因此,理解IsRestoringProvider是理解 Preact Query 持久化能力(@tanstack/preact-query-persist-client配合@tanstack/query-persist-client-core)如何"无痛衔接离线恢复与在线查询"的钥匙——它不仅是暴露给外部的一个 Provider,更是整个恢复流程与数据获取管线之间的协调中枢。
【免费下载链接】query🤖 Powerful asynchronous state management, server-state utilities and data fetching for the web. TS/JS, React Query, Solid Query, Svelte Query and Vue Query.项目地址: https://gitcode.com/GitHub_Trending/qu/query
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考