管理对同一个 Apollo 突变的多次调用

Posted

技术标签:

【中文标题】管理对同一个 Apollo 突变的多次调用【英文标题】:Managing multiple calls to the same Apollo mutation 【发布时间】:2020-03-13 05:35:10 【问题描述】:

因此,请查看文档 https://www.apollographql.com/docs/react/data/mutations/#tracking-loading-and-error-states 中的 Apollo useMutation 示例

function Todos() 
...
  const [
    updateTodo,
     loading: mutationLoading, error: mutationError ,
  ] = useMutation(UPDATE_TODO);
...

  return data.todos.map(( id, type ) => 
    let input;

    return (
      <div key=id>
        <p>type</p>
        <form
          onSubmit=e => 
            e.preventDefault();
            updateTodo( variables:  id, type: input.value  );

            input.value = '';
          
        >
          <input
            ref=node => 
              input = node;
            
          />
          <button type="submit">Update Todo</button>
        </form>
        mutationLoading && <p>Loading...</p>
        mutationError && <p>Error :( Please try again</p>
      </div>
    );
  );

这似乎有一个重大缺陷(imo),更新任何待办事项都会显示每个待办事项的加载状态,而不仅仅是具有待处理突变的待办事项。

这似乎源于一个更大的问题:无法跟踪对同一突变的多次调用的状态。因此,即使我确实只想显示实际加载的待办事项的加载状态,也没有办法做到这一点,因为我们只有“正在加载”而不是“正在加载待办事项 X”的概念。

除了在 Apollo 之外手动跟踪加载状态之外,我能看到的唯一不错的解决方案是拆分一个单独的组件,使用它来渲染每个 Todo,而不是将代码直接放在 Todos 组件中,并让这些组件各自初始化它们的自己的突变。我不确定我认为这是一个好还是坏的设计,但无论哪种情况,我都不觉得我应该改变组件的结构来实现这一点。

这也延伸到错误处理。如果我更新一个待办事项,然后在第一次更新正在进行时更新另一个呢?如果第一次调用出错,那在从useMutation 返回的data 中是否可见?第二个电话呢?

有本地 Apollo 方法来解决这个问题吗?如果没有,是否有处理这个问题的选项可能比我提到的更好?

代码沙盒:https://codesandbox.io/s/v3mn68xxvy

【问题讨论】:

我对你的问题有点困惑。并不是说 apollo 没有唯一/多个加载状态,而是您的代码只有一个 mutationLoading。声明你传递给你的待办事项。相同的状态会传递给它们,所以即使 apollo 有你想要的东西,你的代码仍然不会使用它。它至少必须是 loadingId === id 之类的东西。无论如何,通过在项目级别而不是列表级别调用 useMutation 可以完全解决您的问题。这不是一种解决方法,它是完全理智的解决方案 另一方面,apollo 确实会跟踪正在运行的查询。因此您可以将 isLoading 与将哪个变量传递给突变的知识结合起来,尽管我认为这是一个更糟糕的解决方案 我知道当前代码不会利用从 useMutation 返回的任何每个项目的状态。我知道你想做loadingId === id 之类的操作,但loadingId 不可用。 至于“你可以将你的 isLoading 与传递给突变的变量的知识结合起来”,如果同时进行多个突变,那将如何工作?如果我不确切知道哪些突变实际上是 loadingloading 将毫无意义。 至于将突变推到项目级别,我不同意这是一个理智的解决方案,但它确实有缺点。也就是说,父组件失去了对其项目加载状态的任何了解。假设我想在Todos 组件中显示待办事项的加载状态(出于某种原因),我要么失去这种能力,要么需要向Todo 组件添加某种onLoading 回调,并且仍然手动跟踪 Todos 中的加载状态。 【参考方案1】:

诚然,应该重写文档中的示例以使其更清晰。它还有许多其他问题。

useQueryuseMutation 挂钩仅设计用于一次跟踪单个操作的加载、错误和结果状态。操作的变量可能会改变,它可能会被重新获取或附加到使用fetchMore,但最终,您仍然只是处理那个操作。您不能使用单个挂钩来跟踪多个操作的单独状态。为此,您需要多个挂钩。

在这种情况下,如果输入字段是提前知道的,那么你可以在同一个组件中将钩子分成多个:

const [updateA,  loading: loadingA, error: errorA ] = useMutation(YOUR_MUTATION)
const [updateB,  loading: loadingB, error: errorB ] = useMutation(YOUR_MUTATION)
const [updateC,  loading: loadingC, error: errorC ] = useMutation(YOUR_MUTATION)

如果您正在处理可变数量的字段,那么我们必须将此逻辑分解为一个单独的逻辑,因为我们无法在循环内声明挂钩。这不是 Apollo API 的限制,而只是钩子本身的魔法的副作用。

const ToDo = ( id, type ) => 
  const [value, setValue] = useState('')
  const options =  variables =  id, type: value  
  const const [updateTodo,  loading, error ] = useMutation(UPDATE_TODO, options)
  const handleChange = event => setValue(event.target.value)

  return (
    <div>
      <p>type</p>
      <form onSubmit=updateTodo>
        <input
          value=value
          onChange=handleChange
        />
        <button type="submit">Update Todo</button>
      </form>
    </div>
  )


// back in our original component...

return data.todos.map(( id, type ) => (
  <Todo key=id id=id type=type] />
))

【讨论】:

以上是关于管理对同一个 Apollo 突变的多次调用的主要内容,如果未能解决你的问题,请参考以下文章

Apollo:从 cron 作业调用突变服务器端? [复制]

使用突变响应更新 apollo 客户端

如何用 apollo 客户端状态管理替换 vuex?

Apollo mutate 为单个突变调用更新四次

Apollo GraphQL:从服务器调用突变?

使用 Prisma 和 Apollo 调用多个 GraphQL 突变的正确方法是啥