简体   繁体   中英

How do I handle deletes in react-apollo

I have a mutation like

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

and in another location I have a list of elements.

Is there something better I could return from the server, and how should I update the list?

More generally, what is best practice for handling deletes in apollo/graphql?

I am not sure it is good practise style but here is how I handle the deletion of an item in react-apollo with 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);

Here is a similar solution that works without underscore.js. It is tested with react-apollo in version 2.1.1. and creates a component for a delete-button:

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;

All those answers assume query-oriented cache management.

What if I remove user with id 1 and this user is referenced in 20 queries across the entire app? Reading answers above, I'd have to assume I will have to write code to update the cache of all of them. This would be terrible in long-term maintainability of the codebase and would make any refactoring a nightmare.

The best solution in my opinion would be something like apolloClient.removeItem({__typeName: "User", id: "1"}) that would:

  • replace any direct reference to this object in cache to null
  • filter out this item in any [User] list in any query

But it doesn't exist (yet)

It might be great idea, or it could be even worse (eg. it might break pagination)

There is interesting discussion about it: https://github.com/apollographql/apollo-client/issues/899

I would be careful with those manual query updates. It looks appetizing at first, but it won't if your app will grow. At least create a solid abstraction layer at top of it eg:

  • next to every query you define (eg. in the same file) - define function that clens it properly eg

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
}

and then, collect all those updates and call them all in final update

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

For Apollo v3 this works for me:

const [deleteExpressHelp] = useDeleteExpressHelpMutation({
  update: (cache, {data}) => {
    cache.evict({
      id: cache.identify({
        __typename: 'express_help',
        id: data?.delete_express_help_by_pk?.id,
      }),
    });
  },
});

From the new docs :

Filtering dangling references out of a cached array field (like the Deity.offspring example above) is so common that Apollo Client performs this filtering automatically for array fields that don't define a read function.

Personally, I return an int which represents the number of items deleted. Then I use the updateQueries to remove the document(s) from the cache.

I have faced the same issue choosing the appropriate return type for such mutations when the rest API associated with the mutation could return http 204, 404 or 500.

Defining and arbitrary type and then return null (types are nullable by default) does not seem right because you don't know what happened, meaning if it was successful or not.

Returning a boolean solves that issue, you know if the mutation worked or not, but you lack some information in case it didn't work, like a better error message that you could show on FE, for example, if we got a 404 we can return "Not found".

Returning a custom type feels a bit forced because it is not actually a type of your schema or business logic, it just serves to fix a "communication issue" between rest and Graphql.

I ended up returning a string . I can return the resource ID/UUID or simply "ok" in case of success and return an error message in case of error.

Not sure if this is a good practice or Graphql idiomatic.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM