Apollo Client V2 以错误的顺序获取数据

Posted

技术标签:

【中文标题】Apollo Client V2 以错误的顺序获取数据【英文标题】:Apollo Client V2 fetches data the wrong order 【发布时间】:2018-06-30 17:32:38 【问题描述】:

我正在构建一个使用 Json Web 令牌授权的应用程序。我正在使用 Node.js、GraphQL 和 Apollo 客户端 V2(以及其他一些东西,但这里不相关)构建这个应用程序。我创建了一个 login 解析器和一个 currentUser 解析器,让我可以通过 JWT 获取当前用户。我稍后使用该令牌并将其发送到我的授权标头中,结果如下所示:

这样那部分就完成了!但这就是我遇到的麻烦。

我试图解释情况

我在 Apollo Client V2 项目的前端部分使用 React。当我进行登录突变时,我会这样做。使用 formik,我创建了我的 onSubmit:

const response = await mutate(
  variables: 
    email: values.email,
    password: values.password,
  ,
)
const token = response.data.login.jwt
localStorage.setItem('token', token)

history.push('/') // Navigating to the home page

这就是我想要的登录突变(只是令牌):

export const loginMutation = gql`
  mutation($email: String!, $password: String!) 
    login(email: $email, password: $password) 
      jwt
    
  
`

为了获取currentUser 的数据,我将currentUser query 放在了我的根路由器文件中。 请原谅我将组件命名为 PrivateRoute。我还没有重命名它,因为我找不到合适的名称。对不起。所以在/src/router/index.js 我有这个:

// First the actual query
const meQuery = gql`

  currentUser 
    id
    username
    email
  

`

...

// A component that passess the currentUser as a prop, otherwise it will be null
const PRoute = ( component: Component, ...rest ) => 
  return (
    <Route
      ...rest
      render=props => 
        return (
          <Component
            ...props
            currentUser=
              rest.meQuery.currentUser ? rest.meQuery.currentUser : null
            
          />
        )
      
    />
  )


// Another component that uses the meQuery so I later can access it if I use the PrivateRoute component.
const PrivateRoute = graphql(meQuery,  name: 'meQuery' )(PRoute)

// Using the PrivateRoute. And in my Home component I can later grap the currentUser via propsb
const App = () => (
  <Router>
    <div>
      <PrivateRoute exact path="/" component=Home />
...

Home 组件中,我抓住了道具:

const  data:  loading, error, getAllPosts = [], currentUser   = this.props

我将它传递给我的Navbar 组件:

<Navbar currentUser=this.props.currentUser />

Navbar 组件中,如果用户名存在,我将使用它:

const  username  = this.props.currentUser || 

然后我渲染它。

这就是我遇到的麻烦

当我到达/login 路由时,我的应用程序当前正在尝试获取currentUser。在我成功登录后,我取回了令牌,但 currentUser query 不再被获取。因此,我必须刷新我的页面以获取当前用户及其所有值。

我还制作了一个小视频来演示我的问题所在。我相信它会比我尝试输入更清楚地向您显示问题。

这是视频:https://www.youtube.com/watch?v=GyI_itthtaE

我还要感谢您阅读我的帖子,并希望您能帮助我。我不知道为什么这会发生在我身上,我似乎无法解决它。我已尽我所能写出这个问题,如果读起来令人困惑,请见谅。

谢谢

【问题讨论】:

第一次获取令牌不会重新获取查询,您应该这样做或重新获取。其次,有一个错误,即您的查询产生错误后,它不订阅存储。这意味着当您从某个地方重新获取它时,您的 grahql HoC 将不会更新。这就是为什么当前的解决方案是使用skip: 甚至在获取令牌之前不尝试获取用户。在你获取令牌之后,你会改变一些传递给 graphql HoC 的状态或道具,所以它会第一次尝试获取用户。 感谢您回复@RobertSimon!我想在我将我的令牌添加到我的本地存储然后导航到/ 路由之后,它将使用令牌获取 currentUser。您是如何注意到它没有订阅商店的?另外,我注意到您是 Slack 上的Shockitv 先生。你介意在那里聊天吗?我明白如果你没有时间/不想!感谢您再次回复。 【参考方案1】:

我认为您可以通过将查询的 FetchPolicy 设置为 "cache-and-network" 来解决此问题。你可以阅读获取政策here: "GraphQL query options.fetchPolicy"

在你的具体情况下,我认为你可以更新这一行

const PrivateRoute = graphql(meQuery,  name: 'meQuery' )(PRoute)

到这里:

const PrivateRoute = graphql(meQuery,  name: 'meQuery', options: fetchPolicy: 'cache-and-network' )(PRoute)

说明

如文档中所述,默认策略是 cache-first

    currentUser 第一次被查询并更新缓存。 您执行登录突变,缓存未更新 你更新它(阅读它here: "Updating the cache after a mutation")。 currentUser 查询再次执行,但由于默认的 cache-first 策略,将仅从缓存中检索过时的结果。

来自official documentation:

cache-first:这是我们总是尝试读取的默认值 首先从缓存中获取数据。如果满足您所需的所有数据 查询在缓存中,然后将返回该数据。阿波罗将 仅当缓存结果不可用时才从网络获取。这 fetch 策略旨在最小化发送的网络请求数量 渲染你的组件。

cache-and-network:此获取策略将让 Apollo 首先尝试从您的缓存中读取数据。如果满足您所需的所有数据 查询在缓存中,然后将返回该数据。然而, 无论完整数据是否在您的缓存中 fetchPolicy 将始终使用网络接口执行查询 与缓存优先不同,它只会在查询时执行您的查询 数据不在您的缓存中。此获取策略针对用户进行了优化 在尝试保留缓存数据的同时获得快速响应 以额外网络为代价与您的服务器数据一致 请求。

除了这两个之外,还有两个策略:'network-only' 和 'cache-only' 这里是link to the documentation

【讨论】:

您好!谢谢你的详细解释。我尝试了您的解决方案,但不幸的是它没有成功。我仍在努力完成这项工作。好像我永远也解决不了这个问题【参考方案2】:

对我来说,当我在登录突变中重新获取 currentUser 查询时,它起作用了。我在下面添加了我的代码。也许有帮助:

onSubmit(event) 
event.preventDefault();

const  email, password  = this.state;

this.props
  .mutate(
    variables:  email, password ,
    update: (proxy,  data ) => 
      // Get Token from response and set it to the localStorage
      localStorage.setItem('token', data.login.jwt);
    ,
    refetchQueries: [ query: currentUser ]
  )
  .catch((res) => 
    const errors = res.graphQLErrors.map(error => error.message);
    this.setState( errors );
  );

【讨论】:

以上是关于Apollo Client V2 以错误的顺序获取数据的主要内容,如果未能解决你的问题,请参考以下文章

zeit-now v2 + apollo-server-express:操场错误:无法访问服务器

Apollo-upload-client 与 Cloudinary

如何使用 GQL 从 React 前端的 Apollo 客户端获取错误

如何避免在错误响应中将错误集合包装在 Apollo Server V2 中的错误对象中

如何从 graphql apollo 客户端查询中获取/记录/捕获错误

错误:尝试解析模块 @apollo/client React Native 时