在允许查询之前检测 Apollo 是不是已完成配置
Posted
技术标签:
【中文标题】在允许查询之前检测 Apollo 是不是已完成配置【英文标题】:Detecting if Apollo has finished configuration before allowing queries在允许查询之前检测 Apollo 是否已完成配置 【发布时间】:2021-07-26 09:36:53 【问题描述】:我有一个具有以下***结构的 React SPA。我使用 Auth0 进行身份验证,使用 Apollo Client 进行查询,使用 React Context 提供全局状态。
我遇到的问题是,在 Apollo 客户端完成其标头的配置(即,通过 Auth0 插入令牌)之前,在我的全局状态下执行的查询正在执行。
有没有办法(也许使用 useApolloClient 钩子来判断 Apollo 在其 authLink 中是否有令牌?或者另一种方式来实现延迟初始化 Global State 直到其父组件 AuthorizedApolloProvider 完成的目标?
<BrowserRouter>
<Auth0ProviderWithHistory>
<AuthorizedApolloProvider>
<GlobalState>
<App />
</GlobalState>
</AuthorizedApolloProvider>
</Auth0ProviderWithHistory>
</BrowserRouter>
在 AuthorizedApolloProvider 中,我通过在所有查询中插入令牌来为 Apollo 设置上下文,如下所示:
const AuthorizedApolloProvider = (children) =>
const getAccessTokenSilently = useAuth0()
const httpLink = createHttpLink (
uri: uriGQL, // a config parameter
)
const authLink = setContext(async () =>
const token = await getAccessTokenSilently()
console.log('Got the token..... ', token.substring(0, 10))
return
headers:
authorization: token ? token : ""
)
let links = [authLink, httpLink]
const client = new ApolloClient(
link: ApolloLink.from(links),
cache: new InMemoryCache(
addTypename: false,
),
)
console.log('Rendering ApolloProvider')
return(
<ApolloProvider client=client>
children
</ApolloProvider>
)
接下来,在 Global State 中,我发送 GQL 查询以获取有关已登录用户的信息,如下所示:
const GlobalState = props =>
const user: auth0User, isLoading, isAuthenticated = useAuth0()
const client = useApolloClient()
const [state, setState] = useState(initialState)
const [ getUser, loading, data, error ] = useLazyQuery(GET_USER)
const history = useHistory()
useEffect(async () =>
// Has useAuth0 returned?
if(isLoading===false)
console.log('isAuthenticated=', isAuthenticated)
console.log('auth0User', auth0User)
// Is authenticated?
if(!isAuthenticated)
// If not authenticated...
console.log('Not authenticated')
localStorage.removeItem('user')
setState( ...state, loaded: true, user: null )
else
// If authenticated...
// Check to see if user object is in local storage
let user = await localStorage.getItem('user')
if(user)
console.log('Authenticated, found user in local storage')
// User found in local storage
user = JSON.parse(user)
setState( ...state, loaded: true, user: user )
else
// User not found in local storage. Must fetch from server
console.log('Authenticated, getting user from server')
try
console.log('Calling getUser...')
const result = await getUser()
catch (error)
console.log(error)
, [isLoading])
...
我遇到的问题是在 Apollo 客户端完成配置之前调用了 getUser 查询。
当我运行代码时,我看到下面的片段...
try
console.log('Calling getUser...')
const result = await getUser()
...在以下...之前被执行
console.log('Got the token..... ', token.substring(0, 10))
return
headers:
authorization: token ? token : ""
【问题讨论】:
【参考方案1】:您需要以阻塞方式等待getAccessTokenSilently
完成。您可以通过使用状态来实现这一点。
const [token, setToken] = useState();
useEffect(()=>
try
const token = await getAccessTokenSilently(); // you need this to finish before moving on
setToken(token);
catch (e)
//catch errors
, []);
if(!token) return '...loading'; //render loading component
const authLink = setContext(() =>
//token comes from state now & don't need await
)
const links = [authLink, httpLink];
const client = new ApolloClient(
link: ApolloLink.from(links),
cache: new InMemoryCache( addTypename: false )
)
return(
<ApolloProvider client=client>
children
</ApolloProvider>
)
我个人将我的 ApollClient、链接和其他相关的 ApolloConfig 放在不同的文件中,并传递一个令牌来清理一下。
【讨论】:
谢谢一百万。这解决了问题。我现在可以阻止全局状态的渲染,直到 Apollo 完成加载。以上是关于在允许查询之前检测 Apollo 是不是已完成配置的主要内容,如果未能解决你的问题,请参考以下文章
Apollo 查询在页面刷新时运行,但不使用反应路由器导航?
从 apollo 缓存读取查询,查询尚不存在,但所有信息都已存储在缓存中
如何选择性地允许 apollo-server-express 中的查询?