在Apollo 2.0中执行Mutation后设置缓存/全局状态的问题
Got it, let's walk through how to tie together your API login mutation with the local state update in Apollo 2.0. You're already halfway there with the @client mutation for setting isLoggedIn—here's how to connect everything smoothly:
1. 定义后端登录的GraphQL Mutation
First, create the mutation that will hit your backend API to authenticate the user. This should return any necessary data like an auth token or user profile:
# LoginMutation.graphql mutation LoginMutation($username: String!, $password: String!) { login(username: $username, password: $password) { token user { id username } } }
You already have your local state mutation for setting isLoggedIn, but just to recap (in case you need a reference):
# SetLoggedInMutation.graphql mutation SetLoggedInMutation($isLoggedIn: Boolean!) { setLoggedIn(isLoggedIn: $isLoggedIn) @client }
2. 结合两个Mutation:执行API登录并更新本地状态
Now you need to trigger the API mutation first, and on success, fire the local @client mutation to update your global isLoggedIn state. Below are two common approaches depending on whether you're using hooks (Apollo 2.6+) or higher-order components (HOCs, traditional Apollo 2.x):
Option A: Using React Hooks (Apollo 2.6+)
import { useState } from 'react'; import { useMutation } from '@apollo/react-hooks'; import LoginMutation from './LoginMutation.graphql'; import SetLoggedInMutation from './SetLoggedInMutation.graphql'; function LoginForm() { const [username, setUsername] = useState(''); const [password, setPassword] = useState(''); // Hook for API login mutation const [login, { loading, error }] = useMutation(LoginMutation, { onCompleted: (data) => { // 1. Store the auth token (for future requests) localStorage.setItem('authToken', data.login.token); // 2. Update local global state to logged in setLoggedIn({ variables: { isLoggedIn: true } }); // 3. Redirect to your app's main page window.location.href = '/dashboard'; }, onError: (err) => { console.error('Login failed:', err); // Show error message to user (e.g., invalid credentials) } }); // Hook for local state update mutation const [setLoggedIn] = useMutation(SetLoggedInMutation); const handleSubmit = (e) => { e.preventDefault(); login({ variables: { username, password } }); }; return ( <form onSubmit={handleSubmit}> <input type="text" value={username} onChange={(e) => setUsername(e.target.value)} placeholder="Username" required /> <input type="password" value={password} onChange={(e) => setPassword(e.target.value)} placeholder="Password" required /> <button type="submit" disabled={loading}> {loading ? 'Logging in...' : 'Login'} </button> {error && <p style={{ color: 'red' }}>Invalid username or password</p>} </form> ); } export default LoginForm;
Option B: Using Higher-Order Components (HOCs)
If your project still uses the traditional Apollo 2.x HOC pattern:
import { Component } from 'react'; import { graphql, compose } from 'react-apollo'; import LoginMutation from './LoginMutation.graphql'; import SetLoggedInMutation from './SetLoggedInMutation.graphql'; class LoginForm extends Component { state = { username: '', password: '' }; handleSubmit = (e) => { e.preventDefault(); const { username, password } = this.state; this.props.loginMutation({ variables: { username, password } }) .then(({ data }) => { localStorage.setItem('authToken', data.login.token); this.props.setLoggedInMutation({ variables: { isLoggedIn: true } }); window.location.href = '/dashboard'; }) .catch((err) => console.error('Login failed:', err)); }; render() { const { loading, error } = this.props.loginMutation; return ( <form onSubmit={this.handleSubmit}> <input type="text" value={this.state.username} onChange={(e) => this.setState({ username: e.target.value })} placeholder="Username" required /> <input type="password" value={this.state.password} onChange={(e) => this.setState({ password: e.target.value })} placeholder="Password" required /> <button type="submit" disabled={loading}> {loading ? 'Logging in...' : 'Login'} </button> {error && <p style={{ color: 'red' }}>Invalid username or password</p>} </form> ); } } export default compose( graphql(LoginMutation, { name: 'loginMutation' }), graphql(SetLoggedInMutation, { name: 'setLoggedInMutation' }) )(LoginForm);
3. 额外优化:初始化本地状态 & 自动携带认证头
Initialize isLoggedIn on app load
Make sure your local state reflects whether the user is already logged in (e.g., if they have a saved token) when the app starts:
import { ApolloClient } from 'apollo-client'; import { InMemoryCache } from 'apollo-cache-inmemory'; import { HttpLink } from 'apollo-link-http'; const cache = new InMemoryCache({ defaults: { isLoggedIn: !!localStorage.getItem('authToken') // Check for saved token on init } }); const client = new ApolloClient({ link: new HttpLink({ uri: '/your-graphql-endpoint' }), cache });
Auto-include auth token in requests
Use apollo-link-context to automatically add the auth token to all future API requests:
import { setContext } from 'apollo-link-context'; const authLink = setContext((_, { headers }) => { const token = localStorage.getItem('authToken'); return { headers: { ...headers, authorization: token ? `Bearer ${token}` : '' } }; }); const client = new ApolloClient({ link: authLink.concat(new HttpLink({ uri: '/your-graphql-endpoint' })), cache });
内容的提问来源于stack exchange,提问作者Stretch0

