简体   繁体   English

我如何处理反应阿波罗中的删除

[英]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?更一般地说,在 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:我不确定这是一种好的实践风格,但这里是我如何使用 updateQueries 处理 react-apollo 中的项目删除:

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.这是一个类似的解决方案,无需 underscore.js。 It is tested with react-apollo in version 2.1.1.它在版本 2.1.1 中使用react-apollo进行了测试。 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?如果我删除user ID为1并且用户在20个查询整个应用程序引用? 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:我认为最好的解决方案是像apolloClient.removeItem({__typeName: "User", id: "1"})这样的东西:

  • replace any direct reference to this object in cache to null将缓存中对此对象的任何直接引用替换为null
  • filter out this item in any [User] list in any query在任何查询的任何[User]列表中过滤掉此项

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有关于它的有趣讨论: 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:对于 Apollo v3,这对我有用:

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.从缓存的数组字段中过滤悬空引用(如上面的 Deity.offspring 示例)非常普遍,以至于 Apollo Client 会自动为未定义读取函数的数组字段执行此过滤。

Personally, I return an int which represents the number of items deleted.就个人而言,我返回一个int表示删除的项目数。 Then I use the updateQueries to remove the document(s) from the cache.然后我使用updateQueries从缓存中删除文档。

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.当与突变相关的其余 API 可能返回 http 204、404 或 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".返回一个布尔值可以解决这个问题,你知道突变是否有效,但你缺乏一些信息,以防它不起作用,比如你可以在 FE 上显示的更好的错误消息,例如,如果我们得到 404 我们可以返回“未找到”。

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.返回自定义类型感觉有点强迫,因为它实际上不是您的架构或业务逻辑的类型,它只是用于解决 rest 和 Graphql 之间的“通信问题”。

I ended up returning a string .我最终返回了一个 string I can return the resource ID/UUID or simply "ok" in case of success and return an error message in case of error.如果成功,我可以返回资源 ID/UUID 或简单地“确定”,并在出错时返回错误消息。

Not sure if this is a good practice or Graphql idiomatic.不确定这是一个好的做法还是 Graphql 惯用的方法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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