首页 文章

在突变后设置Apollo缓存/状态

提问于
浏览
1

我正在使用Apollo 2.0来管理我的graphQL API调用并处理我的react应用程序的全局状态 .

我正在尝试创建一个登录屏幕,用户输入他们的用户名和密码,这将被发送到我的API进行身份验证,一旦成功,我想将 isLoggedIn 的全局状态设置为 true .

到目前为止,我能够使用一个使用 @client 声明的突变来设置全局状态,因此它只关注本地状态 . 我有另一个突变,它使graphQL API调用并验证用户名/密码,然后返回成功/错误响应 .

我希望能够在API调用突变完成或失败后设置 isLoggedIn .

我的客户端具有以下默认状态和解析器设置如下:

const httpLink = new HttpLink({
  uri: '/graphql',
  credentials: 'same-origin'
});

const cache = new InMemoryCache();

const stateLink = withClientState({
  cache,
  resolvers: {
    Mutation: {
      updateLoggedInStatus: (_, { isLoggedIn }, { cache }) => {
        const data = {
          loggedInStatus: {
            __typename: 'LoggedInStatus',
            isLoggedIn
          },
        };
        cache.writeData({ data });
        return null;
      },
    },
  },
  defaults: {
    loggedInStatus: {
      __typename: 'LoggedInStatus',
      isLoggedIn: false,
    },
  },
});

const link = ApolloLink.from([stateLink, httpLink])

const client = new ApolloClient({
  link,
  cache
});

export default client

然后在我的登录组件中,我有以下突变和查询,我在compose的帮助下作为HOC传递:

const UPDATE_LOGGED_IN_STATUS = gql`
  mutation updateLoggedInStatus($isLoggedIn: Boolean) {
    updateLoggedInStatus(isLoggedIn: $isLoggedIn) @client
  }`

const AUTHENTICATE = gql`
  mutation authenticate($username: String!, $password: String!) {
    auth(username: $username, password: $password) {
      username
      sales_channel
      full_name
      roles
    }
  }`

const GET_AUTH_STATUS = gql`
  query {
    loggedInStatus @client {
      isLoggedIn
    }
  }`

export default compose(
  graphql(GET_AUTH_STATUS, {
    props: ({ data: { loading, error, loggedInStatus } }) => {
      if (loading) {
        return { loading };
      }

      if (error) {
        return { error };
      }

      return {
        loading: false,
        loggedInStatus
      };
    },
  }),
  graphql(UPDATE_LOGGED_IN_STATUS, {
    props: ({ mutate }) => ({
      updateLoggedInStatus: isLoggedIn => mutate({ variables: { isLoggedIn } }),
    }),
  }),
  graphql(AUTHENTICATE, {
    props: ({ mutate }) => ({
      authenticate: (username, password) => mutate({ variables: { username, password } }),
    }),
  })
)(withRouter(Login));

因此,您可以看到我提交了 this.props.authenticate(username, password) ,这是在提交登录表单时使用的 .

然后我有 this.props.updateLoggedInStatus(Boolean) ,我能够更新客户端缓存/状态 .

我如何组合这些以便我可以调用 authenticate() 并且如果它成功,设置 loggedInStatus 并且如果它失败,设置 hasErrorederrorMessage 标志的排序?

提前致谢 .

编辑:

我试图在我的变异回调中处理更新状态 .

// Form submission handler
onSubmit = async ({ username, password }) => {
    this.setState({loading: true})
    this.props.authenticate(username, password)
      .then(res => {
        this.setState({loading: false})
        this.props.updateLoggedInStatus(true)
      })
      .catch(err => {
        this.setState({loading: false, errorMessage: err.message})
        console.log('err', err)
      })
  }

有没有比这更好的方法呢?不得不等待回电感觉非常复杂 . 我原本以为我可以通过我的解析器将响应映射到我的缓存对象?

1 回答

  • 1

    我认为're currently handling it (calling authenticate and then updateLoggedInStatus) is about as clean and simple as you'的方式是 apollo-link-state . 但是,首先使用 apollo-link-state 可能是矫枉过正 . 从Apollo的缓存中导出登录状态可能更简单 . 例如,您可以拥有这样的HOC:

    import client from '../wherever/client'
    
    const withLoggedInUser = (Component) => {
      const user = client.readFragment({
      id: 'loggedInUser', 
      fragment: gql`
        fragment loggedInUser on User { # or whatever your type is called
          username
          sales_channel
          full_name
          roles
          # be careful about what fields you list here -- even if the User
          # is in the cache, missing fields will result in an error being thrown
        }
      `
      })
      const isLoggedIn = !!user
      return (props) => <Component {...props} user={user} isLoggedIn={isLoggedIn}/>
    }
    

    请注意,我使用 loggedInUser 作为密钥 . 这意味着我们在配置 InMemoryCache 时也必须使用 dataIdFromObject

    import { InMemoryCache, defaultDataIdFromObject } from 'apollo-cache-inmemory'
    
    const cache = new InMemoryCache({
      dataIdFromObject: object => {
        switch (object.__typename) {
          case 'User': return 'loggedInUser'
          // other types you don't want the default behavior for
          default: return defaultDataIdFromObject(object);
        }
      }
    })
    

相关问题