如何在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
相关产品推荐
相关产品推荐

