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