React/Apollo 2:存储用户认证状态的正确方式是什么?
嘿,在React/Apollo 2里处理认证状态,我刚好有不少实践经验,结合你现在的提交验证流程,给你梳理几个最靠谱的方案:
一、用Apollo Client缓存存储核心认证状态
Apollo Client自带的InMemoryCache天生适合存这种全局状态,毕竟你已经拿到服务器返回的用户对象了,直接往里存就行:
- 服务器验证通过拿到用户数据后,用
client.writeData()直接写入缓存:
// 假设你已经从服务器响应里拿到了user对象 client.writeData({ data: { isAuthed: true, currentUser: user, }, });
- 之后在组件里,用Apollo 2常用的
graphql高阶组件(或者useQuery钩子,如果你的版本支持)读取状态:
import { graphql } from 'react-apollo'; import gql from 'graphql-tag'; const Dashboard = ({ data: { isAuthed, currentUser } }) => { if (!isAuthed) return <LoginForm />; return <div>欢迎回来,{currentUser.username}!</div>; }; export default graphql(gql` query GetAuthState { isAuthed @client currentUser @client } `)(Dashboard);
- 这种方式的好处是完全融入Apollo生态,状态变更会自动触发组件重渲染,不用额外引入Redux之类的状态管理库。
二、结合localStorage实现状态持久化
如果希望页面刷新后用户不用重新登录,就得把状态持久化到本地存储:
- 验证通过后,除了写入Apollo缓存,把必要的信息(比如用户基本数据、认证token)存到
localStorage:
// 假设服务器返回了token和user对象 localStorage.setItem('authToken', token); localStorage.setItem('currentUser', JSON.stringify(user)); // 同步更新Apollo缓存 client.writeData({ data: { isAuthed: true, currentUser: user } });
- 在应用初始化时(比如App组件的
componentDidMount,或者Apollo Client初始化逻辑里),读取本地存储恢复状态:
// 初始化Apollo Client后执行 const savedUser = localStorage.getItem('currentUser'); const isAuthed = !!savedUser; if (isAuthed) { client.writeData({ data: { isAuthed: true, currentUser: JSON.parse(savedUser), }, }); }
- 注意:敏感信息比如token,优先用
sessionStorage或者服务器设置的HttpOnly Cookie,避免XSS攻击风险。
三、用Apollo Link自动处理认证请求头
既然用了Apollo,就可以通过ApolloLink给所有请求自动带上认证信息,这和认证状态管理是相辅相成的:
- 创建一个auth link,从本地存储或缓存里取token,加到请求头:
import { ApolloLink } from 'apollo-link'; import { createHttpLink } from 'apollo-link-http'; const httpLink = createHttpLink({ uri: '/graphql' }); const authLink = new ApolloLink((operation, forward) => { const token = localStorage.getItem('authToken'); operation.setContext({ headers: { authorization: token ? `Bearer ${token}` : '', }, }); return forward(operation); }); // 合并authLink和httpLink,创建Apollo Client const client = new ApolloClient({ link: authLink.concat(httpLink), cache: new InMemoryCache(), });
- 这样每次发送GraphQL请求都会自动带上认证头,不用在每个mutation/query里手动设置。
四、优化你现有的
updateAuthStatus mutation 如果你已经在用本地mutation更新认证状态,可以把缓存操作和持久化逻辑整合进去:
- 先定义本地mutation:
const UPDATE_AUTH_STATUS = gql` mutation UpdateAuthStatus($isAuthed: Boolean!, $currentUser: User) { updateAuthStatus(isAuthed: $isAuthed, currentUser: $currentUser) @client } `;
- 然后在Apollo Client的resolvers里处理状态更新:
const client = new ApolloClient({ cache: new InMemoryCache(), resolvers: { Mutation: { updateAuthStatus: (_, { isAuthed, currentUser }, { cache }) => { // 更新缓存 cache.writeData({ data: { isAuthed, currentUser }, }); // 处理持久化 if (isAuthed) { localStorage.setItem('currentUser', JSON.stringify(currentUser)); localStorage.setItem('authToken', currentUser.token); // 假设token存在user对象里 } else { localStorage.removeItem('currentUser'); localStorage.removeItem('authToken'); } return { isAuthed, currentUser }; }, }, }, });
- 这样你调用
updateAuthStatusmutation时,就会自动完成缓存更新和持久化,逻辑更集中,也符合Apollo的最佳实践。
内容的提问来源于stack exchange,提问作者sKyLineLOL
相关产品推荐
相关产品推荐

