引言
你是否遇到过这样的情况:用户在"个人中心"修改了头像,切回"首页"时头像却没变?或者用户早上打开了你的 App,下午切回来时看到的还是早上的旧新闻?
这并不是 Bug,而是 React Native 中数据同步机制缺失的表现。为了解决这些问题,我们需要手动配置 TanStack Query 的刷新时机。但最让人头疼的是:到底该监听 Screen 聚焦还是 App 聚焦?这两者有什么区别?
接下来的内容将为你彻底拆解这两个容易混淆的概念,让你根据业务需求精准控制数据刷新。
1. Refresh on Screen Focus (页面级聚焦)
代码片段:
TypeScript
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]),
)
}
-
触发时机: 当你在 App 内部 切换页面时。
- 例如:你有一个底部的 Tab 栏(首页、我的)。
- 动作:用户从"我的"页面点击 Tab 切换回"首页"。
-
依赖库: 依赖
react-navigation(或 Expo Router) 的useFocusEffect。 -
原理:
- React Navigation 会在页面进入视口(Visible)时触发回调。
- 代码逻辑是手动 调用
queryClient.refetchQueries去强制刷新指定的查询(如['posts'])。
-
适用场景:
- 用户在 Tab A 改了数据,切换到 Tab B 时,你希望 Tab B 立即更新。
- 注意 :如果不加这个,在 React Navigation 中,仅仅切换 Tab 通常不会 触发组件的重新挂载(Re-mount),所以
useEffect或useQuery的默认挂载刷新不会执行。你需要这个钩子来手动触发。
2. Refetch on App Focus (应用级聚焦)
代码片段:
TypeScript
import { 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()
}, [])
-
触发时机: 当你 切换 App 或者 锁屏/解锁 时。
- 例如:用户正在用你的 App,突然切出去回了个微信,或者接了个电话,然后又切回你的 App。
-
依赖库: 依赖 React Native 原生的
AppState。 -
原理:
- 监听操作系统的状态变化(Background -> Active)。
- 调用
focusManager.setFocused(true)。这是在告诉 TanStack Query 的全局大脑:"嘿,用户切回 App 了,你可以开始工作了。" - 它不会 指定刷新某一个 Query,而是自动检查所有处于 Active 状态且已过期(Stale)的 Query 并刷新它们。
-
适用场景:
- 保持数据的实时性(如社交媒体的时间流、股票价格)。
- 防止用户长时间挂起 App 后看到过期的旧数据。
核心区别对比表
| 特性 | Refresh on Screen Focus (第一段代码) | Refetch on App Focus (第二段代码) |
|---|---|---|
| 触发动作 | 切换 Tab / 页面跳转 (App 没关,只是切页面) | 切换 App / 锁屏 (App 到了后台又回来) |
| 范围 | 局部:通常只刷新当前页面的数据 | 全局:影响整个 App 所有活跃的查询 |
| 控制方式 | 手动 :代码里写死了要刷新 ['posts'] |
自动 :Query 引擎根据 staleTime 自动决定刷新谁 |
| 核心依赖 | @react-navigation/native |
react-native (AppState) |
| 必须性 | 选配:取决于业务逻辑是否需要切 Tab 刷新 | 标配:建议在根组件配置,赋予 App 原生感知力 |
总结
- 如果你想让用户从微信切回来 时刷新数据,用 Refetch on App Focus (写在
_layout.tsx)。 - 如果你想让用户点底部 Tab 切换 时刷新数据,用 Refresh on Screen Focus(写在具体的页面组件里)。
最佳实践是:两者通常都需要。 全局配置负责 App 级别的活跃检测,局部 Hook 负责具体的页面交互逻辑。