Как обрабатывать удаления в реакции-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)
}