React Native结合Apollo Client实现已登录用户从缓存加载数据的方法
React Native + Apollo Client 登录持久化与缓存优先加载实现方案
核心问题根因
- 当前Apollo缓存仅存储在内存中,应用关闭后缓存即被清空,重开应用必然会触发网络请求
- 登录态判断逻辑不合理:Routes组件直接读取本地存储的token即标记为已登录,未做有效性校验
- 缺少明确的缓存读取策略配置,容易触发不必要的网络请求
分步实现方案
1. 配置Apollo持久化缓存(解决重开应用缓存丢失问题)
- 引入
apollo3-cache-persist配合AsyncStorage,将Apollo内存缓存持久化到本地存储 - 初始化Apollo Client时关联持久化的缓存实例,同时配置全局默认查询策略为
cache-first
初始化代码示例(新建apollo.js作为客户端配置文件):
import { ApolloClient, InMemoryCache } from '@apollo/client'; import { persistCache } from 'apollo3-cache-persist'; import AsyncStorage from '@react-native-async-storage/async-storage'; // 创建缓存实例 const cache = new InMemoryCache(); // 配置缓存持久化,写入AsyncStorage persistCache({ cache, storage: AsyncStorage, }); // 初始化全局Apollo Client export const apolloClient = new ApolloClient({ uri: '你的GraphQL接口地址', cache, // 全局默认配置优先读缓存 defaultOptions: { watchQuery: { fetchPolicy: 'cache-first', }, query: { fetchPolicy: 'cache-first', }, }, });
2. 重构登录态初始化逻辑(解决自动登录校验问题)
- 将本地token读取、有效性校验的逻辑统一放到AuthProvider中,Routes组件仅根据AuthProvider返回的user状态渲染对应页面
- 启动应用时优先读取本地存储的用户凭证,携带凭证向服务端发起登录校验请求,校验通过再标记为已登录
- 退出登录时同时清空本地存储和Apollo缓存,避免多账号切换时的数据串读
AuthProvider关键逻辑修改:
export default function AuthProvider({children}) { const [callData, {loading, data}] = useLazyQuery(AUTH_USER); const [user, setUser] = useState(null); const [info, setInfo] = useState(null); // 增加初始化完成标识,避免页面闪烁 const [isInitComplete, setIsInitComplete] = useState(false); // 启动时自动读取本地存储做登录校验 useEffect(() => { const initAuth = async () => { try { const storedUser = await AsyncStorage.getItem('user'); if (storedUser) { const userInfo = JSON.parse(storedUser); setInfo(userInfo); // 登录校验必须走网络,不能读缓存 await callData({ variables: {name: userInfo.name, key: userInfo.key}, fetchPolicy: 'network-only', }); } } catch (err) { console.log('登录态初始化失败', err); await resetStorage(); } finally { setIsInitComplete(true); } }; initAuth(); }, []); // 校验结果处理逻辑保留原有逻辑 useEffect(() => { if (data) { if (data.UserAuth.AuthStatus) { setUser(true); } else { resetStorage(); setUser(false); } } }, [data]); // 启动阶段未完成初始化时显示加载态 if (loading || !isInitComplete) { return ( <Center> <ActivityIndicator size="large" color="#0000ff" /> </Center> ); } return ( <MyContext.Provider value={{ user, info, LogIn: async (name, passw) => { // 原有登录逻辑保留 }, LogOut: async () => { setUser(null); setInfo(null); await AsyncStorage.removeItem('user'); // 退出登录时清空全量Apollo缓存 await apolloClient.clear(); }, }}> {children} </MyContext.Provider> ); }
Routes组件简化逻辑(移除原有的本地存储读取,统一由AuthProvider处理):
export default function Routes() { const {user} = useContext(MyContext); return <>{user ? <AppTabs /> : <AuthTabs />}</>; }
3. 配置AppTabs的缓存优先读取规则
- 全局默认启用
cache-first策略:优先读取本地缓存的查询数据,仅当缓存不存在时才发起网络请求 - 等待用户信息(请求变量)加载完成后再发起业务查询,避免空变量导致的无效请求
- 移除手动调用
client.readQuery的冗余逻辑,useQuery会自动按照配置的策略读取缓存
AppTabs关键逻辑修改:
export default function AppTabs() { const {info} = useContext(MyContext); const {loading, error, data} = useQuery(GET_USER, { variables: info, fetchPolicy: 'cache-first', // 显式指定优先读缓存 skip: !info, // 变量未加载完成时跳过请求 }); if (loading || !data) { return ( <Center> <ActivityIndicator size="large" color="#0000ff" /> </Center> ); } // 原有业务逻辑保留 }
注意事项
- 缓存匹配规则:只有当查询的GraphQL文档、请求变量和之前写入缓存时完全一致时,才会命中缓存
- 登录校验特殊规则:校验接口的fetchPolicy必须设置为
network-only,不能走缓存,避免token过期后仍被判定为登录有效 - 缓存清理规则:退出登录、用户权限变更时需要主动调用
apolloClient.clear()清空全量缓存
内容的提问来源于stack exchange,提问作者Petr Špác
相关产品推荐
相关产品推荐

