React Native离线重启Context值未加载及重复渲染问题求助
修复React Native离线模式下Context状态丢失与无限重渲染问题
核心问题分析
- NetInfo监听用法错误:你把
NetInfo.addEventListener返回的取消订阅函数当成了状态获取函数调用,导致监听立即被移除,网络状态永远是初始的false,且无法监听后续变化。 - 离线数据判断逻辑缺陷:用
JSON.stringify(offlineData) !== storedData做比较,不仅性能差,还可能因为数组元素的顺序、格式问题导致误判,触发不必要的setOfflineData,进而引发重渲染循环。 - 异步初始化逻辑混乱:多个异步操作并行执行,没有处理竞态,且缺少必要的状态更新判断(比如权限状态已经是
true时,没必要再调用setState)。
修复后的完整代码
import React, { createContext, useState, useEffect } from 'react'; import NetInfo from '@react-native-community/netinfo'; import AsyncStorage from '@react-native-async-storage/async-storage'; // 假设这些key是你定义的常量 const loginKey = 'USER_LOGIN_KEY'; const offlineKey = 'OFFLINE_DATA_KEY'; interface AppContextValue { login: string; setLogin: (loggedIn: string) => void; hasInternetConnection: boolean; setInternetConnection: (hasConnection: boolean) => void; hasCameraPermissions: boolean; setCameraPermissions: (hasCameraPermissions: boolean) => void; hasLocationPermissions: boolean; setLocationPermissions: (hasLocationPermissions: boolean) => void; offlineData: any[]; setOfflineData: (offlineData: any[]) => void; } const AppContext = createContext<AppContextValue>({ login: '', setLogin: () => {}, hasInternetConnection: false, setInternetConnection: () => {}, hasCameraPermissions: false, setCameraPermissions: () => {}, hasLocationPermissions: false, setLocationPermissions: () => {}, offlineData: [], setOfflineData: () => {}, }); interface AppProviderProps { children: React.ReactNode; } // 假设这两个权限请求函数是你定义的 const requestCameraPermission = async (): Promise<boolean> => { // 你的权限请求逻辑 const result = await /* 实际权限请求代码 */; return result === 'granted'; }; const requestLocationPermission = async (): Promise<boolean> => { // 你的权限请求逻辑 const result = await /* 实际权限请求代码 */; return result === 'granted'; }; export const AppProvider: React.FC<AppProviderProps> = ({ children }) => { const [login, setLogin] = useState(''); const [hasInternetConnection, setInternetConnection] = useState(false); const [hasCameraPermissions, setCameraPermissions] = useState(false); const [hasLocationPermissions, setLocationPermissions] = useState(false); const [offlineData, setOfflineData] = useState<any[]>([]); // 存储离线数据到AsyncStorage useEffect(() => { const storeOfflineData = async () => { try { if (offlineData.length > 0) { await AsyncStorage.setItem(offlineKey, JSON.stringify(offlineData)); } } catch (error) { console.error('Error storing offline data:', error); } }; storeOfflineData(); }, [offlineData]); // 初始化所有状态:网络、登录、权限、离线数据 useEffect(() => { // 注册网络状态监听 const unsubscribeNetInfo = NetInfo.addEventListener((state) => { // 只有当状态变化时才更新,避免不必要的重渲染 if (state.isConnected !== hasInternetConnection) { setInternetConnection(!!state.isConnected); } }); // 统一处理所有异步初始化逻辑 const initializeApp = async () => { // 1. 获取存储的登录信息 const userNumber = await AsyncStorage.getItem(loginKey); if (userNumber && userNumber !== login) { setLogin(userNumber); } // 2. 检查相机权限 const cameraPermission = await requestCameraPermission(); if (cameraPermission !== hasCameraPermissions) { setCameraPermissions(cameraPermission); } // 3. 检查位置权限 const locationPermission = await requestLocationPermission(); if (locationPermission !== hasLocationPermissions) { setLocationPermissions(locationPermission); } // 4. 获取离线数据:仅在初始加载时覆盖,避免循环 const storedData = await AsyncStorage.getItem(offlineKey); if (storedData) { const parsedData = JSON.parse(storedData); // 简单比较长度,避免JSON.stringify的性能问题 if (parsedData.length !== offlineData.length) { setOfflineData(parsedData); } } }; initializeApp(); // 组件卸载时清理网络监听 return () => { unsubscribeNetInfo(); }; }, [login, hasCameraPermissions, hasLocationPermissions, offlineData.length]); return ( <AppContext.Provider value={{ login, hasInternetConnection, setLogin, setInternetConnection, hasCameraPermissions, setCameraPermissions, hasLocationPermissions, setLocationPermissions, offlineData, setOfflineData, }} > {children} </AppContext.Provider> ); }; export default AppContext;
关键修复点说明
- NetInfo监听修复:不再调用返回的取消订阅函数,而是保存该函数在组件卸载时执行,确保网络状态可以被正确监听,同时增加状态变化判断,避免不必要的更新。
- 异步初始化逻辑合并:将所有异步操作放在同一个
initializeApp函数中,避免并行执行的竞态问题,同时每个状态更新前都做了当前值对比,减少不必要的重渲染。 - 离线数据判断优化:用数组长度对比替代
JSON.stringify,既提升性能,又避免因序列化格式差异导致的误判;且仅在初始加载时覆盖数据,避免循环触发存储逻辑。 - 权限请求优化:权限请求后对比当前状态,只在状态变化时调用
setState,减少无效重渲染。
内容的提问来源于stack exchange,提问作者user18370307
相关产品推荐
相关产品推荐

