Установка Apollo кеша / состояния после мутации - PullRequest
0 голосов
/ 25 апреля 2018

Я использую Apollo 2.0 для управления вызовами API GraphQL и для обработки глобального состояния моего реагирующего приложения.

Я пытаюсь создать экран входа в систему, где пользователь вводит свое имя пользователя и пароль.отправленный в мой API для аутентификации, и после успеха я хочу установить глобальное состояние isLoggedIn на true.

. Пока я могу установить глобальное состояние с одной мутацией, которая использует@client декларация, так что это касается только местного государства.У меня есть другая мутация, которая вызывает API-интерфейс graphQL и проверяет имя пользователя / пароль, а затем возвращает ответы об успехе / ошибке.

Я хочу иметь возможность установить isLoggedIn после завершения или сбоя мутации вызова API.

Мой клиент имеет следующее состояние по умолчанию и распознаватели, установленные так:

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

Затем в моем компоненте Login у меня есть следующие мутации и запросы, которые я передаю как HOC с помощью compose:

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, а если не получится, установите своего рода флаг hasErrored или errorMessage?

Заранее спасибо.

РЕДАКТИРОВАТЬ:

Я попытался обновить состояние в обратном вызове моей мутации.

// 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 Ответ

0 голосов
/ 25 апреля 2018

Я думаю, что способ, которым вы в настоящее время обрабатываете это (вызов authenticate, а затем updateLoggedInStatus) примерно такой же чистый и простой, как вы собираетесь получить с apollo-link-state. Тем не менее, использование apollo-link-state для этого, вероятно, излишне. Вероятно, было бы проще получить статус входа в систему из кеша Аполлона. Например, у вас может быть 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 в качестве ключа. Это означает, что мы также должны использовать dataIdFromObject при настройке InMemoryCache:

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);
    }
  }
})
...