在允许查询之前检测 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 缓存读取查询,查询尚不存在,但所有信息都已存储在缓存中

如何选择性地允许 apollo-server-express 中的查询?

React Apollo Client - 在查询数据进入缓存之前修改它

如何检测 Vue $router.back() 是不是已完成,而不是历史堆栈为空的情况?