You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在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:

实现登录流程:API认证 + 全局状态更新

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 08:24:31