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

如何在App组件首次渲染前执行Apollo GraphQL查询?(组件依赖结果)

解决Apollo查询在App组件中的权限路由问题

你遇到的两个问题本质是:

  • useQuery 钩子必须在 ApolloProvider 包裹的组件树内部 使用,否则无法获取到Apollo客户端实例
  • React组件不能定义为异步函数,因为组件的返回值必须是JSX元素,而async函数返回的是Promise,React无法直接渲染

下面是两种可行的解决方法:

方法一:将查询逻辑封装到ApolloProvider内部的子组件

把权限校验和路由跳转的逻辑抽成一个独立组件,放在ApolloProvider内部,这样就能正常使用useQuery:

// 先定义一个权限校验组件
const AuthChecker: FC = () => {
  const { loading, error, data } = useQuery<{ user: User }>(whoAmIQuery);
  const history = useHistory();

  // 处理加载状态
  if (loading) return <div>加载中...</div>;
  // 处理错误(比如未登录)
  if (error || !data?.user) {
    history.push('/login');
    return null;
  }

  // 根据用户权限跳转对应页面
  useEffect(() => {
    if (data.user.role === 'admin') {
      history.push('/admin');
    } else {
      history.push('/dashboard');
    }
  }, [data.user, history]);

  return null; // 仅做跳转逻辑,无需渲染内容
};

// 修改App组件
export const App: FC = () => {
  return (
    <div className="app">
      <CookiesProvider>
        <Router history={browserHistory}>
          <ApolloProvider client={client}>
            <Suspense fallback={<div />}>
              <Switch>
                <Route path="/login" exact>
                  <LoginPage />
                </Route>
                {/* 新增全局路由处理权限校验 */}
                <Route path="/" exact>
                  <AuthChecker />
                </Route>
                {/* 其他受保护路由 */}
                <Route path="/admin" component={AdminPage} />
                <Route path="/dashboard" component={DashboardPage} />
              </Switch>
            </Suspense>
          </ApolloProvider>
        </Router>
      </CookiesProvider>
    </div>
  );
};

方法二:使用useEffect手动调用client.query

如果你不想新增组件,可以在App组件里用useEffect结合已创建的Apollo客户端实例直接发起查询,手动管理状态:

export const App: FC = () => {
  const [loading, setLoading] = useState(true);
  const [user, setUser] = useState<User | null>(null);
  const [error, setError] = useState<Error | null>(null);
  const history = useHistory();

  useEffect(() => {
    const fetchUser = async () => {
      try {
        const result = await client.query<{ user: User }>({ query: whoAmIQuery });
        setUser(result.data.user);
      } catch (err) {
        setError(err as Error);
      } finally {
        setLoading(false);
      }
    };

    fetchUser();
  }, []);

  // 根据状态处理路由跳转
  useEffect(() => {
    if (!loading) {
      if (error || !user) {
        history.push('/login');
      } else if (user.role === 'admin') {
        history.push('/admin');
      } else {
        history.push('/dashboard');
      }
    }
  }, [loading, user, error, history]);

  // 加载状态占位
  if (loading) return <div>加载中...</div>;

  return (
    <div className="app">
      <CookiesProvider>
        <Router history={browserHistory}>
          <ApolloProvider client={client}>
            <Suspense fallback={<div />}>
              <Switch>
                <Route path="/login" exact>
                  <LoginPage />
                </Route>
                <Route path="/admin" component={AdminPage} />
                <Route path="/dashboard" component={DashboardPage} />
              </Switch>
            </Suspense>
          </ApolloProvider>
        </Router>
      </CookiesProvider>
    </div>
  );
};

注意:第二种方法直接使用预先创建的client实例发起查询,无需依赖ApolloProvider,但要确保client实例已正确配置(比如携带credentials)。

内容的提问来源于stack exchange,提问作者Petruza

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 14:05:18