Как обрабатывать удаления в реакции-apollo
У меня есть мутация типа
mutation deleteRecord($id: ID) {
deleteRecord(id: $id) {
id
}
}
а в другом месте у меня есть список элементов.
Есть ли что-то лучшее, что я мог бы вернуть с сервера, и как мне обновить список?
В более общем плане, что лучше всего подходит для обработки удалений в apollo/graphql?
Ответы
Ответ 1
Я не уверен, что это стиль хорошей практики, но вот как я обрабатываю удаление элемента в response-apollo с updateQueries:
import { graphql, compose } from 'react-apollo';
import gql from 'graphql-tag';
import update from 'react-addons-update';
import _ from 'underscore';
const SceneCollectionsQuery = gql `
query SceneCollections {
myScenes: selectedScenes (excludeOwner: false, first: 24) {
edges {
node {
...SceneCollectionScene
}
}
}
}`;
const DeleteSceneMutation = gql `
mutation DeleteScene($sceneId: String!) {
deleteScene(sceneId: $sceneId) {
ok
scene {
id
active
}
}
}`;
const SceneModifierWithStateAndData = compose(
...,
graphql(DeleteSceneMutation, {
props: ({ mutate }) => ({
deleteScene: (sceneId) => mutate({
variables: { sceneId },
updateQueries: {
SceneCollections: (prev, { mutationResult }) => {
const myScenesList = prev.myScenes.edges.map((item) => item.node);
const deleteIndex = _.findIndex(myScenesList, (item) => item.id === sceneId);
if (deleteIndex < 0) {
return prev;
}
return update(prev, {
myScenes: {
edges: {
$splice: [[deleteIndex, 1]]
}
}
});
}
}
})
})
})
)(SceneModifierWithState);
Ответ 2
Вот аналогичное решение, которое работает без underscore.js. Протестировано с react-apollo
в версии 2.1.1. и создает компонент для кнопки удаления:
import React from "react";
import { Mutation } from "react-apollo";
const GET_TODOS = gql'
{
allTodos {
id
name
}
}
';
const DELETE_TODO = gql'
mutation deleteTodo(
$id: ID!
) {
deleteTodo(
id: $id
) {
id
}
}
';
const DeleteTodo = ({id}) => {
return (
<Mutation
mutation={DELETE_TODO}
update={(cache, { data: { deleteTodo } }) => {
const { allTodos } = cache.readQuery({ query: GET_TODOS });
cache.writeQuery({
query: GET_TODOS,
data: { allTodos: allTodos.filter(e => e.id !== id)}
});
}}
>
{(deleteTodo, { data }) => (
<button
onClick={e => {
deleteTodo({
variables: {
id
}
});
}}
>Delete</button>
)}
</Mutation>
);
};
export default DeleteTodo;
Ответ 3
Лично я возвращаю int
, который представляет количество удаленных элементов. Затем я использую updateQueries
для удаления документов из кеша.
Ответ 4
Все эти ответы предполагают управление кешем, ориентированное на запросы.
Что если я удаляю user
с идентификатором 1
и на него ссылаются в 20 запросах по всему приложению? Читая ответы выше, я должен был предположить, что мне придется писать код для обновления кэша всех из них. Это было бы ужасно в плане долгосрочной ремонтопригодности кодовой базы и превращало бы любой рефакторинг в кошмар.
На мой взгляд, лучшим решением было бы что-то вроде apolloClient.removeItem({__typeName: "User", id: "1"})
которое бы:
- заменить любую прямую ссылку на этот объект в кэше на
null
- отфильтровать этот элемент в любом списке
[User]
в любом запросе
Но его не существует (пока)
Это может быть отличной идеей, или это может быть еще хуже (например, это может нарушить нумерацию страниц)
Об этом есть интересная дискуссия: https://github.com/apollographql/apollo-client/issues/899
Я был бы осторожен с этими обновлениями запросов вручную. Сначала это выглядит аппетитно, но не будет, если ваше приложение будет расти. По крайней мере, создайте сплошной слой абстракции поверх него, например:
- рядом с каждым определенным вами запросом (например, в том же файле) - определите функцию, которая очищает его правильно, например
const MY_QUERY = gql'';
// it local 'cleaner' - relatively easy to maintain as you can require proper cleaner updates during code review when query will change
export function removeUserFromMyQuery(apolloClient, userId) {
// clean here
}
а затем собрать все эти обновления и вызвать их в окончательном обновлении
function handleUserDeleted(userId, client) {
removeUserFromMyQuery(userId, client)
removeUserFromSearchQuery(userId, client)
removeIdFrom20MoreQueries(userId, client)
}