在允许查询之前检测 Apollo 是否已完成配置

Detecting if Apollo has finished configuration before allowing queries

我有一个具有以下 top-level 结构的 React SPA。我使用 Auth0 进行身份验证,使用 Apollo Client 进行查询,使用 React Context 提供全局状态。

我遇到的问题是,在 Apollo Client 可以完成其 header 配置(即通过 Auth0 插入令牌)之前,我的全局状态中的查询正在执行。

有没有办法(也许使用 useApolloClient 挂钩来判断 Apollo 在其 authLink 中是否有令牌?或者另一种方法来实现 objective 延迟全局状态初始化直到其 parent组件 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>
  )
}

接下来,在全局状态中,我将发送 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 : ""
      }
    }

您需要以阻塞方式等待 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 放在不同的文件中,并向其传递一个令牌以稍微清理一下。