已嵌套Auth0Provider仍提示未包裹的React错误修复咨询
修复方案
问题根源
- Hook调用位置违规:你在
Wrapper组件的顶层(Auth0Provider还未渲染的阶段)直接将useEnvironment()作为RelayEnvironmentProvider的props参数调用,而useEnvironment内部的useAuth0必须依赖Auth0Provider提供的上下文才能工作,此时上下文未初始化,因此抛出未包裹Auth0Provider的错误。 - 异步token未触发环境更新:原代码中
getAccessTokenSilently是异步操作,useMemo会直接用初始空值创建Relay环境,后续token获取成功后也不会重新生成环境,导致请求携带无效token。
具体修复步骤
1. 调整Hook调用位置,确保在Auth0Provider上下文内调用useEnvironment
修改Wrapper组件,新增内部组件封装RelayEnvironmentProvider,让useEnvironment在Auth0Provider的上下文范围内执行:
export const Wrapper: FC<{ children: ReactNode }> = ({ children }) => ( <StrictMode> <GlobalStyle /> <Auth0Provider domain={import.meta.env.VITE_AUTH_DOMAIN} clientId={import.meta.env.VITE_AUTH_CLIENT_ID} authorizationParams={{ redirect_uri: window.location.origin, }} > <Suspense fallback={<Spin />}> <BrowserRouter> {/* 新增内部组件,在Auth0Provider上下文内调用useEnvironment */} <RelayEnvWrapper> {children} </RelayEnvWrapper> </BrowserRouter> </Suspense> </Auth0Provider> </StrictMode> ); // 内部组件:确保useEnvironment在Auth0Provider上下文内执行 const RelayEnvWrapper: FC<{ children: ReactNode }> = ({ children }) => { const environment = useEnvironment(); return ( <RelayEnvironmentProvider environment={environment}> {children} </RelayEnvironmentProvider> ); };
2. 修复useEnvironment的异步token逻辑,实现环境动态更新
使用useState存储token,配合useEffect异步获取token,让token变化时触发useMemo重新创建有效的Relay环境:
import { useMemo, useState, useEffect } from "react"; import { useAuth0 } from "@auth0/auth0-react"; import { Environment, Network, RecordSource, Store, Observable, RequestParameters, Variables, FetchFunction, } from "relay-runtime"; import { createClient } from "graphql-sse"; const fetchFn = (accessToken: string): FetchFunction => { return async (request, variables) => { // 保留你的fetch逻辑,示例补充token携带逻辑 const resp = await fetch("YOUR_GRAPHQL_ENDPOINT", { method: "POST", headers: { "Content-Type": "application/json", Authorization: accessToken ? `Bearer ${accessToken}` : "", }, body: JSON.stringify({ query: request.text, variables, }), }); return await resp.json(); }; }; const subscribeFn: any = (operation: RequestParameters, variables: Variables) => { // 保留你的订阅逻辑,示例补充token携带逻辑 const client = createClient({ url: "YOUR_GRAPHQL_SSE_ENDPOINT", headers: () => ({ Authorization: `Bearer ${localStorage.getItem("access_token")}`, }), }); return new Observable((sink) => { return client.subscribe( { query: operation.text!, variables, }, sink, ); }); }; const createRelayEnvironment = (accessToken: string) => new Environment({ network: Network.create(fetchFn(accessToken), subscribeFn), store: new Store(new RecordSource()), }); export const useEnvironment = () => { const { getAccessTokenSilently } = useAuth0(); const [accessToken, setAccessToken] = useState<string>(""); useEffect(() => { const fetchToken = async () => { try { const token = await getAccessTokenSilently(); setAccessToken(token); // 订阅逻辑需要时,将token存入localStorage供其使用 localStorage.setItem("access_token", token); } catch (error) { console.error("Error fetching access token:", error); } }; fetchToken(); }, [getAccessTokenSilently]); return useMemo(() => { return createRelayEnvironment(accessToken); }, [accessToken]); };
关键说明
- React Hook必须在组件顶层作用域调用,且必须在对应的Provider上下文范围内执行,否则无法获取上下文数据。
- 异步操作获取的状态需要用
useState存储,才能触发组件重新渲染,进而更新依赖该状态的计算值(比如这里的Relay环境)。
内容的提问来源于stack exchange,提问作者kumar425
相关产品推荐
相关产品推荐

