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

时间:2019-11-17 19:53:17

标签: javascript reactjs graphql apollo

因此,请看文档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),更新任何待办事项都将显示每个待办事项的加载状态,而不仅仅是具有待定突变的待办事项。

enter image description here

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

除了手动跟踪Apollo之外的加载状态外,我看到的唯一体面的解决方案是拆分一个单独的组件,使用该组件呈现每个Todo,而不是直接在Todos组件中包含该代码,并让这些组件各自初始化其代码。自己的突变。我不确定这是好的设计还是坏的设计,但是在任何一种情况下,我都不认为必须更改组件的结构来实现此目的。

这也扩展到错误处理。如果我更新一个待办事项,然后在进行第一次更新时又更新另一个,该怎么办?如果首次通话出错,在data返回的useMutation中是否完全可见?那第二通电话呢?

是否有本地的Apollo方法来解决此问题?如果没有,有没有比我上面提到的更好的处理方法了?

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

1 个答案:

答案 0 :(得分:3)

诚然,文档中的示例应重写为更加清晰。还有很多其他问题。

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] />
))