Expo 进阶指南:赋予 TanStack Query “原生感知力” —— 深度解析 AppState 与 NetInfo

在 Web 开发中,我们习惯了浏览器的"智能":切换标签页时页面会自动刷新,断网重连后请求会自动重试。这是因为浏览器底层自动处理了窗口聚焦(Window Focus)和网络状态(Online Status)的事件。

然而,在 React Native (Expo) 环境中,App 默认是"盲"和"聋"的。TanStack Query 不知道用户什么时候把 App 切到了后台,也不知道手机什么时候断了网。

为了让 App 拥有极致的用户体验(如:切回来自动刷新、断网自动暂停重试),我们需要手动配置 "感官系统" 。本文将深入解读 AppStateStatussetOnline,并提供生产环境的完整配置方案。


一、 深度解读: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 递给你的一把"开关"。

逻辑流程图:从断网到重连

lua 复制代码
+---------------------------+
| 📡 物理层                  |
| 手机 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

TypeScript 复制代码
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 就失去了灵魂:

  1. AppStateStatus + focusManager :让 App 知道"我醒了",从而实现微信切回来的自动刷新。
  2. setOnline + onlineManager :让 App 知道"我有网了",从而实现走出电梯后的自动重连。

只要在 _layout.tsx 中写好配置代码,你应用里所有的 useQuery 就都自动拥有了这些原生级别的感知能力。这就是从"能用"到"好用"的关键跨越。

相关推荐
ywf12151 小时前
前端的dist包放到后端springboot项目下一起打包
前端·spring boot·后端
恋猫de小郭1 小时前
2026,Android Compose 终于支持 Hot Reload 了,但是收费
android·前端·flutter
hpoenixf7 小时前
2026 年前端面试问什么
前端·面试
还是大剑师兰特7 小时前
Vue3 中的 defineExpose 完全指南
前端·javascript·vue.js
泯泷7 小时前
阶段一:从 0 看懂 JSVMP 架构,先在脑子里搭出一台最小 JSVM
前端·javascript·架构
mengchanmian8 小时前
前端node常用配置
前端
华洛8 小时前
利好打工人,openclaw不是企业提效工具,而是个人助理
前端·javascript·产品经理
xkxnq8 小时前
第六阶段:Vue生态高级整合与优化(第93天)Element Plus进阶:自定义主题(变量覆盖)+ 全局配置与组件按需加载优化
前端·javascript·vue.js
A黄俊辉A9 小时前
vue css中 :global的使用
前端·javascript·vue.js
小码哥_常10 小时前
被EdgeToEdge适配折磨疯了,谁懂!
前端