You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.30 23:19:09