尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

React Native 中集成 TanStack Query(React Query):焦点刷新、在线状态与订阅控制的完整实战指南

React Native 中集成 TanStack Query(React Query):焦点刷新、在线状态与订阅控制的完整实战指南 React Native 中集成 TanStack QueryReact Query焦点刷新、在线状态与订阅控制的完整实战指南【免费下载链接】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/queryReact QueryTanStack Query被设计为可以在 React Native 中开箱即用。本指南基于当前仓库的框架文档与源码系统讲解在 React Native 应用中落地 TanStack Query 时需要补齐的关键能力把 Web 端的窗口焦点刷新、断网重连自动请求迁移到 AppState 与网络监听以及如何在屏幕Screen层级精确控制查询的刷新与订阅。读完本文你将掌握onlineManager、focusManager、subscribed选项的正确用法并能直接复用仓库中官方示例应用的完整封装模式。React Native 与 Web 环境的差异为什么要做额外适配TanStack Query 在 Web 浏览器中默认就具备两套自动重新请求能力窗口重新聚焦时刷新window focus refetching用户切走又切回页面若数据已过期会自动在后台请求最新数据断网重连时刷新reconnect refetching网络从离线恢复在线时自动重新请求。然而这两套能力在实现上都依赖浏览器全局对象。以仓库中 onlineManager.ts 的默认事件监听为例this.#setup (onOnline) { // addEventListener does not exist in React Native, but window does if (typeof window ! undefined window.addEventListener) { const onlineListener () onOnline(true) const offlineListener () onOnline(false) window.addEventListener(online, onlineListener, false) window.addEventListener(offline, offlineListener, false) return () { window.removeEventListener(online, onlineListener) window.removeEventListener(offline, offlineListener) } } return }focusManager.ts 的默认实现同样监听window上的visibilitychange事件而当手动聚焦状态未设置时其isFocused()判断会退化为读取globalThis.document?.visibilityState——源码注释中明确指出document global can be unavailable in react nativefocusManager.ts。结论很清晰React Native 没有window/document也没有online/offline浏览器事件需要开发者用 RN 生态对应的模块把在线状态和焦点状态喂给 TanStack Query 的两个 Manager。框架文档 react-native.md 给出的正是这套适配方案Web 端对应的完整背景可对照参考 window-focus-refetching.md。React Native 场景下的开发者工具DevTools选项框架文档将 React Native 的调试工具归类为几种社区方案供不同工具链的团队选用注意它们均为第三方项目接入与维护需以各自项目自身为准Rozenite Plugin面向使用 React Native DevTools 的用户的第三方插件支持在官方调试器中直接查看 TanStack Query 状态Native macOS App面向任意基于 JS 的应用的调试桌面应用适合希望在原生调试视图中观察查询状态的场景Flipper Plugin面向 Facebook Flipper 用户的第三方插件Reactotron Plugin面向 Reactotron 用户的第三方插件例如在 Reactotron 时间线中观察查询缓存与事件流。选用时可根据团队当前使用的 RN 调试基础设施官方 DevTools、Flipper 还是 Reactotron决定避免引入多套调试器叠加。在线状态管理让断网重连自动刷新在 RN 中生效TanStack Query 通过单例onlineManageronlineManager.ts统一维护是否在线状态。setEventListener用于替换默认事件源它会先执行上一次监听器的清理函数再注册新的监听onlineManager.ts。当网络状态变化时调用setOnline(boolean)内部只会在状态确实改变时才通知所有订阅者onlineManager.ts从而触发重连后重新请求的行为。方案一使用 react-native-community/netinfo文档给出的标准做法是在模块顶层或应用初始化处用 NetInfo 替换默认事件源import NetInfo from react-native-community/netinfo import { onlineManager } from tanstack/react-query onlineManager.setEventListener((setOnline) { return NetInfo.addEventListener((state) { setOnline(!!state.isConnected) }) })NetInfo.addEventListener返回的取消订阅函数直接作为setEventListener的回调返回值恰好与onlineManager期望的返回清理函数签名一致——当再次调用setEventListener时旧的 NetInfo 监听会被妥善移除。方案二使用 expo-network使用 Expo 时没有 NetInfo 依赖可以改监听expo-network的事件。由于网络状态事件可能存在先注册、后补发初始值的时序问题文档示例用initialised标志做了一次兜底查询import { onlineManager } from tanstack/react-query import * as Network from expo-network onlineManager.setEventListener((setOnline) { let initialised false const eventSubscription Network.addNetworkStateListener((state) { initialised true setOnline(!!state.isConnected) }) Network.getNetworkStateAsync() .then((state) { if (!initialised) { setOnline(!!state.isConnected) } }) .catch(() { // getNetworkStateAsync can reject on some platforms/SDK versions }) return eventSubscription.remove })先用addNetworkStateListener订阅状态变化事件一旦收到首个事件就将initialised置为true。与此同时主动调用getNetworkStateAsync()查询当前网络状态仅当事件监听尚未触发过即首次同步初始化时才回填在线状态避免某些平台或 SDK 版本上事件未及时派发导致的初始状态缺失。仓库示例中的增强版本仓库内的官方 React Native 示例工程 useOnlineManager.ts 在上述模式上做了两处值得借鉴的增强import * as React from react import NetInfo from react-native-community/netinfo import { onlineManager } from tanstack/react-query import { Platform } from react-native export function useOnlineManager() { React.useEffect(() { // React Query already supports on reconnect auto refetch in web browser if (Platform.OS ! web) { return NetInfo.addEventListener((state) { onlineManager.setOnline( state.isConnected ! null state.isConnected Boolean(state.isInternetReachable), ) }) } }, []) }一是用Platform.OS ! web做守卫RN Web 场景保留浏览器默认行为无需覆盖二是在isConnected之外额外要求isInternetReachable避免连着 WiFi 但实际无外网的假在线状态触发无意义的重新请求。该 hook 在 App.tsx 的组件根部被调用即完成了全应用级别的在线状态桥接。App 级焦点管理回到前台时自动刷新Web 端默认通过visibilitychange感知页面可见性React Native 则通过内置的AppState模块获取前台/后台信息。文档建议监听AppState的change事件在应用回到active状态时同步给focusManagerimport { useEffect } from react import { AppState, Platform } from react-native import type { AppStateStatus } from react-native import { focusManager } from tanstack/react-query function onAppStateChange(status: AppStateStatus) { if (Platform.OS ! web) { focusManager.setFocused(status active) } } useEffect(() { const subscription AppState.addEventListener(change, onAppStateChange) return () subscription.remove() }, [])理解这段代码需要看到 focusManager.ts 的底层语义setFocused(focused?: boolean)只在状态确实变化时才派发事件并调用所有监听器focusManager.ts当应用进入active时setFocused(true)会触发一次全局重新聚焦此时仍处于过期stale状态的查询便会按refetchOnWindowFocus的逻辑在后台重新请求——该选项默认值为true可对照 window-focus-refetching.md 中refetchOnWindowFocus: false // default: true的注释。如果你不希望所有查询都跟随应用回到前台而刷新可以在 QueryClient 的defaultOptions.queries里全局关闭refetchOnWindowFocus或在具体useQuery上单独关闭。示例工程将这一逻辑进一步封装成了可复用的 useAppState.tsimport { useEffect } from react import { AppState } from react-native import type { AppStateStatus } from react-native export function useAppState(onChange: (status: AppStateStatus) void) { useEffect(() { const subscription AppState.addEventListener(change, onChange) return () { subscription.remove() } }, [onChange]) }随后在 App.tsx 中定义onAppStateChange并调用useAppState(onAppStateChange)组件卸载时监听会自动移除避免重复订阅或内存泄漏。Screen 级焦点刷新进入页面时重新拉取过期数据AppState只能感知整个 App的前后台无法感知导航栈里当前显示的是哪个 Screen。当业务希望在用户重新回到某个页面时刷新该页的数据例如从详情页返回列表页需要借助 React Navigation 的useFocusEffect。文档推荐版本刷新所有激活的过期查询以下自定义 hook 会在 Screen 每次重新获得焦点时refetch所有处于激活状态且已过期的查询import React from react import { useFocusEffect } from react-navigation/native import { useQueryClient } from tanstack/react-query export function useRefreshOnFocus() { const queryClient useQueryClient() const firstTimeRef React.useRef(true) useFocusEffect( React.useCallback(() { if (firstTimeRef.current) { firstTimeRef.current false return } // refetch all stale active queries queryClient.refetchQueries({ queryKey: [posts], stale: true, type: active, }) }, [queryClient]), ) }代码中的refetchQueries是QueryClient提供的命令式刷新 API实现见 queryClient.ts这里三个过滤条件的含义分别是queryKey: [posts]限定只处理该键或满足该前缀匹配的查询stale: true只处理数据已过期的查询避免无谓请求type: active只处理当前存在活跃订阅者有组件正在观察的查询不打扰后台预取或缓存中的查询。为什么要跳过第一次聚焦因为useFocusEffect不仅在 Screen 重新聚焦时会调用回调在 Screen 初次挂载时也会调用一次。初次挂载时数据通常本来就需要通过queryFn首次加载没有必要再叠加一次refetch因此用firstTimeRef跳过首次只在离开后再次回来时刷新。示例工程版本按页面精准刷新仓库示例 useRefreshOnFocus.ts 采用参数化设计把触发刷新的动作下放给调用方import * as React from react import { useFocusEffect } from react-navigation/native export function useRefreshOnFocus(refetch: () void) { const enabledRef React.useRef(false) useFocusEffect( React.useCallback(() { if (enabledRef.current) { refetch() } else { enabledRef.current true } }, [refetch]), ) }使用时页面把useQuery返回的refetch直接传入见 MoviesListScreen.tsxconst { isPending, error, data, refetch } useQueryMovie[], Error({ queryKey: [movies], queryFn: fetchMovies, }) // ... useRefreshOnFocus(refetch)两种版本各有适用场景文档版本适合多个查询共享同一前缀、回到页面希望整体对齐的场景示例工程版本则粒度更细配合enabledRef同样实现了首次聚焦挂载跳过、后续聚焦才刷新的语义。焦点外的 Screen 暂停订阅subscribed选项如果不想让某些查询在 Screen 失焦后继续保持活动例如还订阅着缓存更新、甚至触发定时刷新TanStack Query 提供了subscribed选项。该选项设置为false时组件会从查询中退订unsubscribe不再触发重新渲染也不会为该 Screen 拉取新数据当它重新变为true例如 Screen 重新获得焦点后查询会重新订阅并保持最新。结合 React Navigation 的useIsFocused可以实现焦点感知的订阅控制import React from react import { useIsFocused } from react-navigation/native import { useQuery } from tanstack/react-query import { Text } from react-native function MyComponent() { const isFocused useIsFocused() const { dataUpdatedAt } useQuery({ queryKey: [key], queryFn: () fetch(...), subscribed: isFocused, }) return TextDataUpdatedAt: {dataUpdatedAt}/Text }subscribed 的底层机制该选项是react-query在QueryObserverOptions之上为useQuery/useInfiniteQuery等 Hook 增加的订阅开关类型定义见 types.ts默认值为true。核心实现位于 useBaseQuery.tsconst subscribed options.subscribed ! false // ... defaultedOptions._optimisticResults isRestoring ? isRestoring : subscribed ? optimistic : undefined // ... const shouldSubscribe !isRestoring subscribed React.useSyncExternalStore( React.useCallback( (onStoreChange) { const unsubscribe shouldSubscribe ? observer.subscribe(notifyManager.batchCalls(onStoreChange)) : noop // ... }, // ... ), )从源码可以清楚地看到两点订阅控制当subscribed false时组件走noop分支不再调用observer.subscribe即没有活跃的 observer 挂到该查询上后续缓存更新不会触发该组件重渲染查询也会因没有活跃订阅者而停止被该页面驱动去获取数据乐观状态未订阅时_optimisticResults不会被置为optimistic因此退订状态下不会乐观地把查询显示为 fetching 中的状态。仓库配套的单元测试验证了这些行为见 useQuery.test.tsx当subscribed为false时查询缓存中该查询的observers.length为 0、queryFn完全不会被调用切换回true后订阅恢复查询可再次正常触发。使用限制与注意点subscribed是useQuery级别的选项但在useQueries中它只作为顶层选项出现不支持在数组内逐条查询设置见 useQueries.tsuseSuspenseQueries不支持该选项需使用常规的useQueries相关类型注释见 useSuspenseQueries.ts当subscribed: false时若页面还依赖初次数据渲染例如首屏需要立刻展示需自行配合placeholderData或缓存中已有数据因为退订状态下查询不会主动发起首次请求。完整组装一个生产可用的 RN 应用骨架把上述能力组合起来参考示例工程 App.tsx一个同时具备断网重连刷新 App 回到前台刷新能力的根组件大致如下import * as React from react import { AppStateStatus, Platform } from react-native import { NavigationContainer } from react-navigation/native import { QueryClient, QueryClientProvider, focusManager, } from tanstack/react-query import { useAppState } from ./src/hooks/useAppState import { useOnlineManager } from ./src/hooks/useOnlineManager function onAppStateChange(status: AppStateStatus) { // React Query already supports in web browser refetch on window focus by default if (Platform.OS ! web) { focusManager.setFocused(status active) } } const queryClient new QueryClient({ defaultOptions: { queries: { retry: 2 } }, }) export default function App() { useOnlineManager() // 断网重连自动刷新仅非 Web 生效 useAppState(onAppStateChange) // App 回到前台自动刷新 return ( QueryClientProvider client{queryClient} NavigationContainer {/* 你的导航栈内部 Screen 可再结合 useRefreshOnFocus / subscribed 做页面级控制 */} /NavigationContainer /QueryClientProvider ) }各层职责清晰分层全局层App根部onlineManagerfocusManager桥接 RN 的系统级事件保证缓存数据在重连、回前台时自动对齐页面层useRefreshOnFocus处理从其他 Screen 返回的场景subscribeduseIsFocused处理失焦页面不再保持订阅的资源节省场景数据层refetchQueries的stale/type: active过滤条件保证每次刷新都只命中真正需要的查询。这套组合正是官方 React Native 示例examples/react/react-native采用的工程范式可直接作为新项目的脚手架参考。小结TanStack Query 之所以能在 React Native 中开箱即用是因为其核心设计把浏览器环境依赖全部收敛在onlineManager与focusManager两个可替换的事件源单例中。开发者只需按本文模式用 RN 生态模块替换默认事件源即可在原生环境中完整获得 Web 端同款的自动刷新能力能力Web 端默认事件源React Native 替代方案仓库证据重连自动刷新window的online/offlineonlineManager.setEventListener NetInfo / expo-networkonlineManager.ts回前台自动刷新document的visibilitychangefocusManager.setFocusedAppStatefocusManager.ts页面聚焦刷新—useFocusEffectrefetchQueriesstale/type: activequeryClient.ts失焦页暂停更新—subscribed选项 useIsFocuseduseBaseQuery.ts调试时可按团队既有工具链在上述社区 DevTools 方案中选择其一需要进一步验证行为时可直接运行仓库中的 React Native 示例工程并参考其src/hooks下各 hook 的写法抽取到自己的项目中使用。【免费下载链接】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),仅供参考
返回列表