React+Apollo Client 2+Graphcool登录状态无法响应式更新求助
解决React + Apollo Client 2登录/登出后UI不自动更新的问题
你的代码功能正常,但登录或登出后UI没自动更新,核心原因是**LoginQuery只在组件挂载时执行了一次**,登录/登出后用户状态变化了,但组件没有重新获取最新的用户数据,所以需要手动触发查询更新或者让Apollo自动响应缓存变化。
下面是具体的修复方案:
1. 登录成功后触发查询重新获取用户数据
登录成功后,调用LoginQuery的refetch方法,让Apollo重新发送查询请求,获取最新的用户状态,触发UI更新:
修改你的_confirm方法:
_confirm = async e => { e.preventDefault(); const result = await this.props.LoginMutation({ variables: { email: '5@gmail.com', password: 'password', }, }); const token = result.data.authenticateUser.token; this._saveUserData(token); // 登录成功后重新获取用户数据 await this.props.LoginQuery.refetch(); };
2. 登出时清除缓存并重新查询
登出时,除了删除localStorage的token,还需要让Apollo Client清除之前的用户缓存,并重新执行LoginQuery,这样UI会切换回登录表单:
首先要让组件拿到Apollo Client实例,通过withApollo高阶组件包裹:
import { graphql, compose, withApollo } from 'react-apollo'; // ... const LoginContainer = compose( withApollo, // 添加这一行,让组件拿到client实例 graphql(LoginMutation, { name: 'LoginMutation' }), graphql(LoginQuery, { name: 'LoginQuery' }), )(Login);
然后修改render里的登出按钮逻辑:
<button onClick={async () => { localStorage.removeItem('auth-token'); // 重置Apollo缓存(清除之前的用户数据) await this.props.client.resetStore(); }}> Logout </button>
3. 可选:让查询自动监听缓存变化
你还可以给LoginQuery设置fetchPolicy: 'cache-and-network',这样组件每次渲染时都会先从缓存取数据,再发送网络请求更新缓存,确保数据是最新的:
修改graphql(LoginQuery, ...)的配置:
graphql(LoginQuery, { name: 'LoginQuery', options: { fetchPolicy: 'cache-and-network', }, }),
完整修改后的代码
import React from 'react'; import { graphql, compose, withApollo } from 'react-apollo'; import gql from 'graphql-tag'; class Login extends React.Component { constructor(props) { super(props); this._confirm = this._confirm.bind(this); } _confirm = async e => { e.preventDefault(); const result = await this.props.LoginMutation({ variables: { email: '5@gmail.com', password: 'password', }, }); const token = result.data.authenticateUser.token; this._saveUserData(token); // 登录成功后重新获取用户数据 await this.props.LoginQuery.refetch(); }; _saveUserData = token => { localStorage.setItem('auth-token', token); }; render() { return ( <div> {this.props.LoginQuery.user ? ( <button onClick={async () => { localStorage.removeItem('auth-token'); // 重置缓存,清除用户数据 await this.props.client.resetStore(); }}> Logout </button> ) : ( <form onSubmit={this._confirm}> <input type="email" id="email" /> <input type="password" id="password" /> <button type="submit">Login</button> </form> )} </div> ); } } const LoginMutation = gql` mutation authenticateUser($email: String!, $password: String!) { authenticateUser(email: $email, password: $password) { token } } `; const LoginQuery = gql` query AppQuery { user { id } } `; const LoginContainer = compose( withApollo, graphql(LoginMutation, { name: 'LoginMutation' }), graphql(LoginQuery, { name: 'LoginQuery', options: { fetchPolicy: 'cache-and-network', // 可选,确保数据最新 }, }), )(Login); export default LoginContainer;
原理说明
- 登录后调用
refetch:强制Apollo重新发送查询请求,获取登录后的用户数据,触发组件重新渲染。 - 登出时调用
resetStore:清空Apollo的所有缓存,让后续的查询重新从服务器获取数据(此时因为没有token,user会返回null),从而切换到登录表单。 cache-and-network策略:让组件每次都先展示缓存数据,再用最新的网络数据更新,避免UI闪烁的同时保证数据时效性。
内容的提问来源于stack exchange,提问作者Evanss
相关产品推荐
相关产品推荐

