Кэширует ли Apollo возвращенные данные из мутации

#javascript #reactjs #graphql #react-apollo #apollo-client

#javascript #reactjs #graphql #реагировать-аполлон #apollo-клиент

Вопрос:

Я использую клиент Apollo в приложении React, и мне нужно выполнить мутацию, а затем сохранить возвращенные данные для последующего использования (но у меня не будет доступа к переменным), должен ли я использовать другое решение для управления состоянием или мы можем сделать это в Apollo?

Я читал о том, как сделать это с помощью запроса, но не мутации.

Вот мой код на данный момент

 // Mutation
const [myMutation, { data, errors, loading }] = useMutation(MY_MUTATION, {
    onCompleted({ myMutation }) {
      console.log('myMutation: ', myMutation.dataToKeep);
      if (myMutation amp;amp; myMutation.dataToKeep)
        SetResponse(myMutation.dataToKeep);
    },
    onError(error) {
      console.error('error: ', error);
    },
  });

//How I call it

  onClick={() => {
    myMutation({
      variables: {
        input: {
          phoneNumber: '0000000000',
          id: '0000',
        },
      },
    });
  }}

 

Редактировать:

вот такая мутация

 export const MY_MUTATION = gql`
  mutation MyMutation($input: MyMutationInput!) {
    myMutation(input: $input) {
      dataToKeep
      expiresAt
    }
  }
`;
 

и схема этой мутации

 MyMutationInput:
  phoneNumber: String!
  id: String!

MyMutationPayload:
  dataToKeep
  expiresAt
  
 

Комментарии:

1. какую версию apollo-client вы используете? а также что вы подразумеваете под последующим использованием? вы имеете в виду, что глобальное состояние сохраняется в приложении или больше похоже на состояние, которое должно быть общим для разных компонентов?

2. @ManuelPamplona Я использую 3.2.9

Ответ №1:

Случай 1: Полезная нагрузка использует общие объекты

Проще говоря, кэш клиента Apollo хранит все, что получено от запросов и мутаций, хотя схема должна включать id: ID! поля, и любой запрос должен использовать оба поля id и __typename на соответствующих узлах, чтобы клиент знал, какую часть кэша обновлять.

Это предполагает, что полезная нагрузка мутации является общими данными из схемы, которые могут быть получены с помощью обычного запроса. Это наилучший сценарий.

Учитывая следующую схему на сервере:

 type User {
  id: ID!
  phoneNumber: String!
}

type Query {
  user(id: String!): User!
}

type UpdateUserPayload {
  user: User!
}

type Mutation {
  updateUser(id: String!, phoneNumber: String!): UpdateUserPayload!
}
 

И предполагается, что на клиенте используется кеш:

 import { InMemoryCache, ApolloClient } from '@apollo/client';

const client = new ApolloClient({
  // ...other arguments...
  cache: new InMemoryCache(options)
});
 
  1. Кэш генерирует уникальный идентификатор для каждого идентифицируемого объекта, включенного в ответ.
  2. Кэш хранит объекты по идентификатору в плоской справочной таблице.
  3. Всякий раз, когда входящий объект сохраняется с тем же идентификатором, что и существующий объект, поля этих объектов объединяются.
    • Если входящий объект и существующий объект совместно используют какие-либо поля, входящий объект перезаписывает кэшированные значения для этих полей.
    • Сохраняются поля, которые отображаются только в существующем объекте или только в входящем объекте.

Нормализация создает частичную копию вашего графика данных на вашем клиенте в формате, оптимизированном для чтения и обновления графика по мере изменения состояния вашего приложения.

Мутация клиента должна быть

 mutation UpdateUserPhone($phoneNumber: String!, $id: String!) {
  updateUser(id: $id, phoneNumber: $phoneNumber) {
    user {
      __typename  # Added by default by the Apollo client
      id          # Required to identify the user in the cache
      phoneNumber # Field that'll be updated in the cache
    }
  }
}
 

Тогда любой компонент, использующий этого пользователя через тот же клиент Apollo в приложении, будет обновляться автоматически. Ничего особенного делать не нужно, клиент будет использовать кеш по умолчанию и запускать рендеринг при каждом изменении данных.

 import { gql, useQuery } from '@apollo/client';

const USER_QUERY = gql`
  query GetUser($id: String!) {
    user(id: $id) {
      __typename
      id
      phoneNumber
    }
  }
`;

const UserComponent = ({ userId }) => {
  const { loading, error, data } = useQuery(USER_QUERY, {
    variables: { id: userId },
  });

  if (loading) return null;
  if (error) return `Error! ${error}`;

  return <div>{data.user.phoneNumber}</div>;
}
 

По умолчанию используется fetchPolicy параметр cache-first .


Случай 2: Полезная нагрузка — это пользовательские данные, специфичные для мутации

Если данные фактически недоступны в другом месте схемы, невозможно будет автоматически использовать кэш Apollo, как описано выше.

Используйте другое решение для управления состоянием

Пара вариантов:

Вот пример из документации Apollo GraphQL с использованием localStorage :

 const [login, { loading, error }] = useMutation(LOGIN_USER, {
  onCompleted({ login }) {
    localStorage.setItem('token', login.token);
    localStorage.setItem('userId', login.id);
  }
});
 

Определите схему на стороне клиента

Это чистое решение Apollo GraphQL, поскольку клиент также является библиотекой управления состоянием, которая предоставляет полезные инструменты для разработчиков и помогает анализировать данные.

  1. Создайте локальную схему.
     // schema.js
    export const typeDefs = gql`
      type DataToKeep {
        # anything here
      }
    
      extend type Query {
        dataToKeep: DataToKeep # probably nullable?
      }
    `;
     
  2. Инициализировать пользовательский кеш
     // cache.js
    export const dataToKeepVar = makeVar(null);
    
    export const cache = new InMemoryCache({
      typePolicies: {
        Query: {
          fields: {
            dataToKeep: {
              read() {
                return dataToKeepVar();
              } 
            },
          }
        }
      }
    });
     
  3. Примените переопределение схемы при инициализации клиента
     import { InMemoryCache, Reference, makeVar } from '@apollo/client';
    import { cache } from './cache';
    import { typeDefs } from './schema';
    
    const client = new ApolloClient({
      cache,
      typeDefs,
      // other options like, headers, uri, etc.
    });
     
  4. Следите за изменениями в мутации:
     const [myMutation, { data, errors, loading }] = useMutation(MY_MUTATION, {
      onCompleted({ myMutation }) {
        if (myMutation amp;amp; myMutation.dataToKeep)
          dataToKeepVar(myMutation.dataToKeep);
      }
    });
     
  5. Затем запросите @client поле.
     import { gql, useQuery } from '@apollo/client';
    
    const DATA_QUERY = gql`
      query dataToKeep {
        dataToKeep @client {
          # anything here
        }
      }
    `;
    
    const AnyComponent = ({ userId }) => {
      const { loading, error, data } = useQuery(DATA_QUERY);
    
      if (loading) return null;
      if (error) return `Error! ${error}`;
    
      return <div>{JSON.stringify(data.dataToKeep)}</div>;
    }
     

См. Также Документацию по управлению локальным состоянием.

Комментарии:

1. «Тогда любой компонент, использующий этого пользователя через тот же клиент Apollo в приложении, будет обновляться автоматически». И как мне получить доступ к этому пользователю в кэше?

2. @Mel ничего особенного не требуется, я добавил простой пример, который будет использовать кеш по умолчанию.

3. Когда я создаю новый запрос в соответствии с вашим примером, я получаю «сообщение»: «Поле ‘myMutation’ не существует для типа’Query'», потому что в API нет запроса с этим именем. Чего мне не хватает?

4. @Mel хорошо, немного покопавшись, я включил 2 дополнительных решения: использование другого решения для управления состоянием, как вы предложили, и использование локального состояния, поскольку клиентская библиотека Apollo также является библиотекой управления состоянием.

5. В итоге я использовал context, но ваш ответ — это именно то, что я хотел сделать в начале, спасибо