网站搜索

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 的博客文章。