React Native
React Query 开箱即用,支持 React Native。
开发者工具(DevTools)支持
React Native 的开发者工具集成有多种可选方案:
Rozenite 插件:面向 React Native DevTools 用户的第三方插件:https://www.rozenite.dev/docs/official-plugins/tanstack-query
原生 macOS 应用:一款面向任意基于 JavaScript 的应用的 React Query 调试工具: https://github.com/LovesWorking/rn-better-dev-tools
Flipper 插件:面向 Flipper 用户的第三方插件: https://github.com/bgaleotti/react-query-native-devtools
Reactotron 插件:面向 Reactotron 用户的第三方插件: https://github.com/hsndmr/reactotron-react-query
在线状态管理
React Query 在 Web 浏览器中已原生支持网络重连时自动重新拉取(auto refetch on reconnect)。
要在 React Native 中实现相同行为,你需使用 React Query onlineManager,如下例所示:
1import NetInfo from '@react-native-community/netinfo'2import { onlineManager } from '@tanstack/react-query'3 4onlineManager.setEventListener((setOnline) => {5 return NetInfo.addEventListener((state) => {6 setOnline(!!state.isConnected)7 })8})或
1import { onlineManager } from '@tanstack/react-query'2import * as Network from 'expo-network'3 4onlineManager.setEventListener((setOnline) => {5 let initialized = false6 7 const eventSubscription = Network.addNetworkStateListener((state) => {8 initialized = true9 setOnline(!!state.isConnected)10 })11 12 Network.getNetworkStateAsync()13 .then((state) => {14 if (!initialized) {15 setOnline(!!state.isConnected)16 }17 })18 .catch(() => {19 // getNetworkStateAsync can reject on some platforms/SDK versions20 })21 22 return eventSubscription.remove23})应用获得焦点时重新拉取
React Native 并不提供对 window 事件监听器的支持,而是通过 AppState 模块 提供应用焦点信息。你可以利用 AppState 的 “change” 事件,在应用状态变为 “active” 时触发更新:
1import { useEffect } from 'react'2import { AppState, Platform } from 'react-native'3import type { AppStateStatus } from 'react-native'4import { focusManager } from '@tanstack/react-query'5 6function onAppStateChange(status: AppStateStatus) {7 if (Platform.OS !== 'web') {8 focusManager.setFocused(status === 'active')9 }10}11 12useEffect(() => {13 const subscription = AppState.addEventListener('change', onAppStateChange)14 15 return () => subscription.remove()16}, [])屏幕获得焦点时刷新
在某些场景下,你可能希望在 React Native 屏幕再次获得焦点时重新拉取查询。
以下自定义 Hook 可在屏幕再次获得焦点时,重新拉取所有处于活动状态且过期的查询:
1import React from 'react'2import { useFocusEffect } from '@react-navigation/native'3import { useQueryClient } from '@tanstack/react-query'4 5export function useRefreshOnFocus() {6 const queryClient = useQueryClient()7 const firstTimeRef = React.useRef(true)8 9 useFocusEffect(10 React.useCallback(() => {11 if (firstTimeRef.current) {12 firstTimeRef.current = false13 return14 }15 16 // refetch all stale active queries17 queryClient.refetchQueries({18 queryKey: ['posts'],19 stale: true,20 type: 'active',21 })22 }, [queryClient]),23 )24}上述代码跳过了首次焦点(即屏幕初次挂载时),因为 useFocusEffect 不仅在屏幕获得焦点时调用回调函数,也会在组件挂载时调用。
在屏幕失焦时禁用查询
如果你不希望某些查询在屏幕失焦期间继续保持“活跃”,可以使用 useQuery 的 subscribed 属性。该属性允许你控制查询是否持续订阅更新。结合 React Navigation 的 useIsFocused,你可实现在屏幕失焦时无缝取消查询订阅:
示例用法:
1import React from 'react'2import { useIsFocused } from '@react-navigation/native'3import { useQuery } from '@tanstack/react-query'4import { Text } from 'react-native'5 6function MyComponent() {7 const isFocused = useIsFocused()8 9 const { dataUpdatedAt } = useQuery({10 queryKey: ['key'],11 queryFn: () => fetch(...),12 subscribed: isFocused,13 })14 15 return <Text>DataUpdatedAt: {dataUpdatedAt}</Text>16}当 subscribed 为 false 时,查询将取消对更新的订阅,不会为此屏幕触发重新渲染,也不会拉取新数据;一旦它再次变为 true(例如屏幕重新获得焦点),查询将重新订阅并保持最新状态。