Expo 进阶指南:赋予 TanStack Query “原生感知力” —— 深度解析 AppState 与 NetInfo
在 Web 开发中,我们习惯了浏览器的“智能”:切换标签页时页面会自动刷新,断网重连后请求会自动重试。这是因为浏览器底层自动处理了窗口聚焦(Window Focus)和网络状态(Online Status)的事件。
然而,在 React Native (Expo) 环境中,App 默认是“盲”和“聋”的。TanStack Query 不知道用户什么时候把 App 切到了后台,也不知道手机什么时候断了网。
为了让 App 拥有极致的用户体验(如:切回来自动刷新、断网自动暂停重试),我们需要手动配置 “感官系统” 。本文将深入解读 AppStateStatus 和 setOnline,并提供生产环境的完整配置方案。
一、 深度解读:App 的“感官系统”
1. 视觉神经:AppStateStatus (应用状态)
AppStateStatus 是 React Native 原生提供的一个状态类型,用于描述 App 当前处于什么处境。
三个核心状态:
-
active(前台/活跃) :用户正在盯着屏幕,App 在最上层运行。这是我们唯一希望触发数据刷新的状态。 -
background(后台) :用户按了 Home 键,或者切换到了微信。App 还在内存中运行,但界面不可见。 -
inactive(非活跃) :App 处于“半死不活”的过渡态(如被来电画面覆盖、拉下了通知栏、iOS 多任务切换界面)。
为什么要配置它?
TanStack Query 有一个核心功能叫 Window Focus Refetching。
-
问题:RN 默认没有
window.onfocus事件。 -
解决:我们需要监听
AppState的变化。当状态变为active时,手动调用focusManager.setFocused(true)。 - 效果:TanStack Query 收到信号后,会立即检查页面上的数据是否过期(Stale)。如果过期,它会在后台悄悄发起请求,用户切回来的一瞬间看到的就是最新数据。
2. 听觉神经:setOnline (联网回调)
setOnline 不是一个你可以直接导入的函数,它是 TanStack Query 的 onlineManager.setEventListener 方法传递给你的一个回调函数(参数)。你可以把它理解为 TanStack Query 递给你的一把“开关”。
逻辑流程图:从断网到重连
+---------------------------+
| 📡 物理层 |
| 手机 4G/WiFi 信号变化 |
+-------------+-------------+
|
v (触发系统事件)
+-------------+-------------+
| 🎧 B: NetInfo 监听器 |
+-------------+-------------+
|
v (获取状态: isConnected)
+-------------+-------------+
| 💻 C: 开发者代码逻辑 |
| (app/_layout.tsx) |
+-------------+-------------+
|
v (调用 setOnline)
+-------------+-------------+
| ⚙️ D: TanStack Query |
| (onlineManager) |
+-------------+-------------+
/ \
/ (false) \ (true)
v v
+---------+ +---------+
| E: 🛑 | | F: 🚀 |
| 暂停 | | 恢复 |
| (Pause) | | (Resume)|
| 停止重试 | | 立即重试 |
+---------+ +---------+
为什么要配置它?
- 省电保护:如果没配置,断网时 TanStack Query 会傻傻地一直重试请求,消耗用户电量。配置后,断网即休眠。
- 体验优化:如果没配置,网络恢复后,App 不会有反应,用户必须手动下拉刷新。配置后,信号恢复的瞬间,数据自动加载成功(“电梯效应”)。
二、 生产环境完整配置 (app/_layout.tsx)
这是融合了上述原理的完整配置代码。请确保你已安装了 @react-native-community/netinfo。
import { useEffect } from 'react';
import { AppState, AppStateStatus, Platform } from 'react-native';
import { Slot } from 'expo-router';
import NetInfo from '@react-native-community/netinfo';
import {
QueryClient,
QueryClientProvider,
focusManager,
onlineManager
} from '@tanstack/react-query';
// =================================================================
// 1. 配置网络监听 (给 App 装上“耳朵”)
// =================================================================
onlineManager.setEventListener((setOnline) => {
// setOnline 是 Query 传进来的一个函数,用来接收网络状态
// 我们使用 NetInfo 来监听真实的网络变化
return NetInfo.addEventListener((state) => {
// state.isConnected 可能为 null,用 !! 强转为 boolean
// 当这里调用 setOnline(true) 时,Query 会立即重试刚才失败的请求
setOnline(!!state.isConnected);
});
});
// =================================================================
// 2. 配置 App 状态监听 (给 App 装上“眼睛”)
// =================================================================
function onAppStateChange(status: AppStateStatus) {
// Web 浏览器会自动处理窗口聚焦,只有原生 App 需要手动处理
if (Platform.OS !== 'web') {
// 核心逻辑:
// 当 AppState 变为 'active' (前台) 时
// 我们手动告诉 Query 的 focusManager:“用户现在聚焦在 App 上了”
// Query 收到信号后,会检查页面数据是否过期 (stale),如果过期则自动 Refetch
focusManager.setFocused(status === 'active');
}
}
// =================================================================
// 3. 初始化 QueryClient (配置全局策略)
// =================================================================
const queryClient = new QueryClient({
defaultOptions: {
queries: {
retry: 2, // 失败后自动重试 2 次
staleTime: 1000 * 60, // 数据 1 分钟内算“新鲜”,切回来也不刷新
// 超过 1 分钟后切回来,会触发自动刷新
},
},
});
export default function RootLayout() {
// 注册 AppState 监听器
useEffect(() => {
// 开始监听系统状态变化
const subscription = AppState.addEventListener('change', onAppStateChange);
// 组件卸载时取消监听,这是防止内存泄漏的标准做法
return () => subscription.remove();
}, []);
return (
{/* Slot 是 Expo Router 的页面入口 */}
);
}
三、 总结
在 React Native 中使用 TanStack Query,90% 的人只做了“安装”,而忽略了“配置” 。
如果不配置这两个管理器,你的 App 就失去了灵魂:
-
AppStateStatus+focusManager:让 App 知道“我醒了”,从而实现微信切回来的自动刷新。 -
setOnline+onlineManager:让 App 知道“我有网了”,从而实现走出电梯后的自动重连。
只要在 _layout.tsx 中写好这 50 行代码,你应用里所有的 useQuery 就都自动拥有了这些原生级别的感知能力。这就是从“能用”到“好用”的关键跨越。