web-dev-qa-db-fra.com

Comment gérer les suppressions dans react-apollo

J'ai une mutation comme

mutation deleteRecord($id: ID) {
    deleteRecord(id: $id) {
        id
    }
}

et dans un autre endroit, j'ai une liste d'éléments.

Y a-t-il quelque chose de mieux que je pourrais retourner du serveur, et comment dois-je mettre à jour la liste?

Plus généralement, quelle est la meilleure pratique pour gérer les suppressions dans apollo/graphql?

20
derekdreery

Je ne suis pas sûr que ce soit style de bonne pratique mais voici comment je gère la suppression d'un élément dans react-apollo avec 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);
15
vwrobel

Voici une solution similaire qui fonctionne sans underscore.js. Il est testé avec react-apollo dans la version 2.1.1. et crée un composant pour un bouton de suppression:

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;
10
sinned

Personnellement, je retourne un int qui représente le nombre d'éléments supprimés. J'utilise ensuite le updateQueries pour supprimer le ou les documents du cache.

4
Siyfion

Toutes ces réponses supposent une gestion du cache orientée requêtes.

Que faire si je supprime user avec l'ID 1 Et que cet utilisateur est référencé dans 20 requêtes sur l'ensemble de l'application? En lisant les réponses ci-dessus, je devrais supposer que je devrai écrire du code pour mettre à jour le cache de chacun d'eux. Ce serait terrible dans la maintenabilité à long terme de la base de code et ferait de toute refactorisation un cauchemar.

À mon avis, la meilleure solution serait quelque chose comme apolloClient.removeItem({__typeName: "User", id: "1"}) qui:

  • remplacer toute référence directe à cet objet dans le cache à null
  • filtrer cet élément dans n'importe quelle liste [User] dans n'importe quelle requête

Mais ça n'existe pas (encore)

Ce pourrait être une excellente idée, ou cela pourrait être encore pire (par exemple, cela pourrait casser la pagination)

Il y a une discussion intéressante à ce sujet: https://github.com/apollographql/apollo-client/issues/899

Je serais prudent avec ces mises à jour manuelles des requêtes. Cela semble appétissant au début, mais ce ne sera pas le cas si votre application grandit. Créez au moins une couche d'abstraction solide au-dessus, par exemple:

  • à côté de chaque requête que vous définissez (par exemple dans le même fichier) - définissez la fonction qui la clenche correctement, par exemple

const MY_QUERY = gql``;

// it's 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
}

puis collectez toutes ces mises à jour et appelez-les toutes dans la mise à jour finale

function handleUserDeleted(userId, client) {
  removeUserFromMyQuery(userId, client)
  removeUserFromSearchQuery(userId, client)
  removeIdFrom20MoreQueries(userId, client)
}
1
pie6k