React Native 数据同步双重奏:深度解析“页面级聚焦”与“应用级聚焦”的区别

引言

你是否遇到过这样的情况:用户在"个人中心"修改了头像,切回"首页"时头像却没变?或者用户早上打开了你的 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),所以 useEffectuseQuery 的默认挂载刷新不会执行。你需要这个钩子来手动触发。

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 负责具体的页面交互逻辑。

相关推荐
Nayana15 小时前
Node.js中常用的异步编程方法
前端
周帝15 小时前
手写一个最简单版本的canvas库
前端
AAA简单玩转程序设计15 小时前
谁说Java枚举只是“常量装盒”?它藏着这些骚操作
java·前端
前端小蜗15 小时前
💰该省省,该花花!我靠白嫖飞书,把“每日生存成本”打了下来
前端·程序员·产品
YaeZed15 小时前
Vue3-父子组件通信
前端·vue.js
优爱蛋白15 小时前
IL-21:后Th1/Th2时代的免疫新星
java·服务器·前端·人工智能·健康医疗
Mintopia15 小时前
💬 从猜想到架构:AI 聊天区域的 Web 设计之道
前端·前端框架·aigc
一过菜只因15 小时前
VUE快速入门
前端·javascript·vue.js
滴滴答答哒15 小时前
Quartz Cron 表达式参考表
前端·css·css3