#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)
});
- Кэш генерирует уникальный идентификатор для каждого идентифицируемого объекта, включенного в ответ.
- Кэш хранит объекты по идентификатору в плоской справочной таблице.
- Всякий раз, когда входящий объект сохраняется с тем же идентификатором, что и существующий объект, поля этих объектов объединяются.
- Если входящий объект и существующий объект совместно используют какие-либо поля, входящий объект перезаписывает кэшированные значения для этих полей.
- Сохраняются поля, которые отображаются только в существующем объекте или только в входящем объекте.
Нормализация создает частичную копию вашего графика данных на вашем клиенте в формате, оптимизированном для чтения и обновления графика по мере изменения состояния вашего приложения.
Мутация клиента должна быть
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, как описано выше.
Используйте другое решение для управления состоянием
Пара вариантов:
- локальное хранилище
- Контекстный API React
- и т.д.
Вот пример из документации Apollo GraphQL с использованием localStorage
:
const [login, { loading, error }] = useMutation(LOGIN_USER, {
onCompleted({ login }) {
localStorage.setItem('token', login.token);
localStorage.setItem('userId', login.id);
}
});
Определите схему на стороне клиента
Это чистое решение Apollo GraphQL, поскольку клиент также является библиотекой управления состоянием, которая предоставляет полезные инструменты для разработчиков и помогает анализировать данные.
- Создайте локальную схему.
// schema.js export const typeDefs = gql` type DataToKeep { # anything here } extend type Query { dataToKeep: DataToKeep # probably nullable? } `;
- Инициализировать пользовательский кеш
// cache.js export const dataToKeepVar = makeVar(null); export const cache = new InMemoryCache({ typePolicies: { Query: { fields: { dataToKeep: { read() { return dataToKeepVar(); } }, } } } });
- Примените переопределение схемы при инициализации клиента
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. });
- Следите за изменениями в мутации:
const [myMutation, { data, errors, loading }] = useMutation(MY_MUTATION, { onCompleted({ myMutation }) { if (myMutation amp;amp; myMutation.dataToKeep) dataToKeepVar(myMutation.dataToKeep); } });
- Затем запросите
@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, но ваш ответ — это именно то, что я хотел сделать в начале, спасибо