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 中的错误对象中