如何从Apollo Query初始化Next.js中React全局用户Context的状态?
解决Next.js中UserContext无法保存GraphQL查询结果的问题
我明白你遇到的问题了——useState的初始值只会在组件首次挂载时被读取一次,后续useQuery返回新的data时,initialUser虽然更新了,但不会触发user状态的重新赋值,这就是为什么第二次渲染的用户信息没被保存到Context里。
正确的实现方式
我们需要用useEffect来监听useQuery返回的data变化,一旦拿到有效的用户数据,就主动更新Context的状态。同时还要处理localStorage中的accessToken,确保GraphQL请求能带上身份凭证。
修改后的UserProvider代码如下:
import { createContext, useState, useEffect, useContext } from 'react'; import { useQuery } from '@apollo/client'; import { MeQuery } from './graphql/queries'; // 替换成你的实际路径 const UserContext = createContext<{ user: User | undefined; setUser: (u: User | undefined) => void; loading: boolean; // 新增加载状态,方便组件处理加载逻辑 }>({ user: undefined, setUser: () => {}, loading: true }); export const UserProvider = ({ children }: { children: React.ReactNode }) => { // 1. 初始化状态,默认是undefined const [user, setUser] = useState<User | undefined>(undefined); // 2. 从localStorage获取token(注意:只在客户端执行) const isClient = typeof window !== 'undefined'; const accessToken = isClient ? localStorage.getItem('accessToken') : null; // 3. 执行GraphQL查询,只有当token存在时才发起请求 const { data, loading, error } = useQuery<{ me: UserResponse }>(MeQuery, { skip: !accessToken, // 没有token就跳过查询 fetchPolicy: 'cache-and-network' // 根据需求调整缓存策略 }); // 4. 监听data变化,更新user状态 useEffect(() => { if (data?.me.user) { setUser(data.me.user); } }, [data]); // 5. 处理error情况,比如token失效时清除用户状态 useEffect(() => { if (error) { setUser(undefined); isClient && localStorage.removeItem('accessToken'); } }, [error]); // 6. 组合Context的值,包含加载状态 const contextValue = { user, setUser, loading: loading && !!accessToken // 只有当有token且正在查询时才显示加载 }; return ( <UserContext.Provider value={contextValue}> {children} </UserContext.Provider> ); }; export const useUser = () => useContext(UserContext);
关键改进点解释
useEffect监听data:当useQuery成功获取到用户数据后,useEffect会触发并调用setUser,把数据保存到Context状态中,这样所有消费Context的组件都能拿到最新的用户信息。- 跳过无token的查询:通过
skip: !accessToken避免在没有登录凭证时发起无效请求。 - 新增加载状态:让组件可以感知查询过程,比如显示加载动画,提升用户体验。
- 错误处理:当查询出错(比如token过期),自动清除本地token和用户状态,避免脏数据残留。
额外注意事项
- 确保你的Apollo Client配置中已经正确处理了
accessToken的传递(比如通过请求头),否则查询会因为身份验证失败返回错误。 - 在Next.js中,由于服务器端渲染时没有
localStorage,所以要通过typeof window !== 'undefined'判断执行环境,避免报错。
内容的提问来源于stack exchange,提问作者Gabe
相关产品推荐
相关产品推荐

