【问题标题】:Managing multiple calls to the same Apollo mutation管理对同一个 Apollo 突变的多次调用
【发布时间】: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 与传递给突变的变量的知识结合起来”,如果同时进行多个突变,那将如何工作?如果我不确切知道哪些突变实际上是 loading,loading 将毫无意义。
  • 至于将突变推到项目级别,我不同意这是一个理智的解决方案,但它确实有缺点。也就是说,父组件失去了对其项目加载状态的任何了解。假设我想在Todos 组件中显示待办事项的加载状态(出于某种原因),我要么失去这种能力,要么需要向Todo 组件添加某种onLoading 回调,并且仍然手动跟踪 Todos 中的加载状态。

标签: javascript reactjs graphql apollo


【解决方案1】:

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

useQuery 和 useMutation 挂钩仅设计用于一次跟踪单个操作的加载、错误和结果状态。操作的变量可能会改变,它可能会被重新获取或附加到使用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] />
))

【讨论】:

    猜你喜欢
    • 2018-08-13
    • 1970-01-01
    • 2017-08-25
    • 2020-01-23
    • 2018-09-21
    • 2021-09-03
    • 2019-10-22
    • 2017-03-12
    • 2020-10-20
    相关资源
    最近更新 更多