Apollo Client 2 React GraphQL受保护路由刷新数据异常问题
我太懂这个痛点了——私有路由手动输URL时拦得住,但一刷新就翻车,明明用户已经登录,却因为Apollo查询初始返回空/undefined,被私有路由的重定向逻辑踢回登录页对吧?这本质是刷新后Apollo缓存清空,查询需要重新拉取数据,但路由守卫没等查询完成就急着做判断了。下面给你几个针对性的解决方案,都是在Apollo Client 2环境下能用的:
1. 让私有路由「等一等」查询结果
核心思路是:私有路由组件必须先发起用户登录状态的查询,并且明确区分加载中、查询成功、查询失败三种状态,不能直接用初始的空数据判断用户未登录。
如果你用的是高阶组件写法(Apollo Client 2常用方式),可以这么写:
import React from 'react'; import { Route, Redirect } from 'react-router-dom'; import { graphql } from 'react-apollo'; import gql from 'graphql-tag'; // 定义查询当前用户的GraphQL语句 const CURRENT_USER_QUERY = gql` query CurrentUser { currentUser { id email } } `; const PrivateRouteComponent = ({ data: { loading, currentUser, error }, component: Component, ...rest }) => { // 查询加载中时,显示过渡状态(别直接重定向!) if (loading) return <div>加载中...</div>; // 查询出错/确实没有用户数据,才重定向到登录页 if (error || !currentUser) { return <Redirect to="/login" />; } // 确认有用户数据,正常渲染目标页面组件 return <Route {...rest} render={(props) => <Component {...props} />} />; }; // 用graphql高阶组件把查询注入到私有路由组件 export default graphql(CURRENT_USER_QUERY)(PrivateRouteComponent);
2. 用缓存持久化减少刷新后的等待
刷新页面后Apollo的内存缓存会清空,导致查询必须重新走网络请求。你可以用apollo-cache-persist把缓存持久化到localStorage里,这样刷新后Apollo会先加载本地缓存的用户数据,几乎瞬间就能拿到结果,避免加载状态的尴尬。
配置步骤很简单:
import { ApolloClient } from 'apollo-client'; import { InMemoryCache } from 'apollo-cache-inmemory'; import { persistCache } from 'apollo-cache-persist'; // 初始化缓存 const cache = new InMemoryCache(); // 配置持久化到localStorage persistCache({ cache, storage: window.localStorage, }); // 创建Apollo Client实例 const client = new ApolloClient({ cache, // 你的其他配置:比如link、uri等 });
之后登录成功时,记得把用户数据写入缓存:
// 登录mutation成功后 client.writeData({ data: { currentUser: 你的用户数据, }, });
3. 用本地状态管理优化登录状态判断
如果不想每次都走GraphQL查询,还可以用Apollo Client 2的apollo-link-state管理本地登录状态,把登录状态存在本地缓存里,查询本地状态的速度会比网络请求快得多,刷新后也能快速拿到状态。
比如定义本地状态的resolver:
import { withClientState } from 'apollo-link-state'; const stateLink = withClientState({ cache, resolvers: { Mutation: { setCurrentUser: (_, { user }, { cache }) => { cache.writeData({ data: { currentUser: user } }); return null; }, }, }, defaults: { currentUser: null, }, });
然后登录成功时调用这个mutation写入状态,私有路由直接查询本地的currentUser即可,完全避免网络请求的延迟。
关键注意点
别把data为空直接等同于用户未登录!Apollo Client 2初始查询时data是个空对象,这时候要靠loading状态区分「正在加载」和「确实无数据」,否则肯定会出现刷新误重定向的问题。
内容的提问来源于stack exchange,提问作者scubadude

