reactjs - React Query - 如何使用列表获取的响应为每个项目调用 `setQueryData`?
我正在获取一个列表以在列表页面中渲染——我们称它们为 Todos。这个获取操作是通过一个自定义的 react-query 钩子完成的,该钩子将响应缓存到 ['todo-list'] queryKey 下。当我获取列表时,会在响应中得到每个 Todo 的所有数据。然后,我通过将响应映射到 <Todo> 组件来渲染列表,并将该项作为 prop 传递。
我遇到的问题是,这些项目在多个不同的地方呈现,每个地方使用的 queryKey 都不一样。所以在我有 ['todo-list'] 的地方,我也有 ['my-todo-list']、['all-todo-list'] 等。这些待办事项可以被“完成”,这会触发一个到后端的 mutation 调用,但会通过 setQueryData 对单个项目进行乐观更新,以便 UI 能立即反映更改,而不必等待 mutation 完成或重新获取列表查询。在多个待办事项列表中进行这种乐观更新相当复杂,也感觉效率不高。
我的问题是,是否有意义将任何待办事项列表查询的响应用于 setQueryData 来处理单个项目?例如,setQueryData(['todo', todo.id], todo)。然后,任何需要渲染该数据的子组件都可以只从 react-query 缓存中获取 Todo 对象,只需将 id 字段作为属性传递。
这在任何方面被认为是反模式或不良做法吗?
这是一个 todo-list 查询的基本示例:
// custom react-query hook
export const useTodos = () => {
const todoQuery = useQuery(
['todo-list'],
() => {
return fetchTodos()
}
)
// Set the todo data in the cache
const queryClient = useQueryClient()
todoQuery.data?.todos?.forEach((todo) => {
queryClient.setQueryData(['todo', todo.id], todo)
})
return todoQuery
}
这是在 Todo 组件中使用的一个非常基础的示例:
// Todo component
function Todo({id}) {
const {data, isLoading} = useTodoById(id)
if (isLoading) {
return <p>Loading...</p>
}
return (
<h1 className="todo">{todo.title}</h1>
)
}
export default Todo
同样值得注意的是,useTodoById 钩子也是一个自定义的 react-query 钩子,用于根据 ID 获取单个 Todo —— 其值会被缓存到 ['todo', todo.id] 的 queryKey 下。它的 staleTime 设置为 10 分钟。
答案#1
你所建议的做法一点也不算不好的实践,实际上,它在 TkDodo 的博客(react-query 维护者)中有提到。你可以采用推送方式或者拉取方式,这两种方式各有优劣,博客中都有解释。我唯一看到的不好的实践是你的 queryKeys,通常建议你使用类似 [‘todos’, ‘lists’] 和 [‘todos’, ‘detail’, 1] 这样的方式,这样你就可以利用 invalidateQuery 的部分匹配功能。我建议查看一下关于有效 query keys 的博客文章。