【问题标题】:Setting Apollo cache / state after mutation突变后设置 Apollo 缓存/状态
【发布时间】:2018-10-05 20:56:17
【问题描述】:

我正在使用 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)
      })
  }

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

【问题讨论】:

    标签: reactjs graphql apollo react-apollo apollo-client


    【解决方案1】:

    我认为您当前处理它的方式(调用身份验证,然后调用 updateLoggedInStatus)与使用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);
        }
      }
    })
    

    【讨论】:

    猜你喜欢
    • 2020-05-09
    • 2022-11-11
    • 1970-01-01
    • 2020-11-27
    • 2020-02-03
    • 2021-03-24
    • 2017-06-21
    • 2018-10-29
    • 2019-11-26
    相关资源
    最近更新 更多