我正在构建一个使用Json Web Tokens授权的应用程序。我使用Node.js,GraphQL和Apollo客户端V2构建此应用程序(以及其他一些东西,但这些并不相关)。我创建了一个login
解析器和一个currentUser
解析器,让我通过JWT获取当前用户。我稍后使用该令牌并将其发送到我的授权标题中,结果类似于:
我使用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
我还要感谢你阅读我的帖子,希望你能帮助我。我不知道为什么会发生这种情况,我似乎无法解决这个问题。我尽可能地尽力写下这个问题,很抱歉读起来很困惑。
由于
答案 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 。
缓存优先:这是我们总是尝试阅读的默认值 首先从缓存中获取数据。如果需要满足您的所有数据 查询在缓存中然后将返回该数据。阿波罗会 如果缓存的结果不可用,则仅从网络中获取。这个 获取策略旨在最小化发送时发送的网络请求数 渲染你的组件。
缓存和网络:此提取策略将让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 });
});
}