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

时间:2018-01-22 06:12:04

标签: node.js reactjs authorization graphql apollo

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

enter image description here 那部分就完成了!但是我遇到了麻烦。

我试图解释这种情况

我使用React作为Apollo Client V2的项目前端部分。当我进行登录变异时,我会这样做。使用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 || {}

然后我渲染它。

这就是我遇到麻烦

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

我还创建了一个小视频来演示我的问题。我相信它会比我尝试输入它更清楚地显示问题。

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

我还要感谢你阅读我的帖子,希望你能帮助我。我不知道为什么会发生这种情况,我似乎无法解决这个问题。我尽可能地尽力写下这个问题,很抱歉读起来很困惑。

由于

2 个答案:

答案 0 :(得分:2)

我认为您可以通过将查询的 FetchPolicy 设置为&#34; cache-and-network&#34; 来解决此问题。您可以阅读有关提取政策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

    第一次查询
  1. currentUser 并更新缓存。
  2. 您执行登录突变,缓存未更新没有 你更新它(阅读它here: "Updating the cache after a mutation")。
  3. currentUser 查询再次执行,但由于默认的缓存优先策略,将仅从缓存中检索过期结果。
  4. 来自official documentation

      

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

         

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

    除了这两项政策外,还有两项政策:&#39;仅限网络&#39;和&#39;仅缓存&#39;这里是link to the documentation

答案 1 :(得分:1)

for me it worked when I refetched the currentUser query in my login mutation. I added my code below. Maybe it helps:

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 });
  });
}