React Query竞态条件问题:Todo应用模态框显示旧数据
React Query Todo应用竞态条件问题解决方案
问题根源分析
- 创建Todo场景:新Todo创建成功后跳转回Todo页面,对应新Todo的查询缓存尚未建立,打开模态框时才触发查询,首次获取数据前模态框拿到的是空值。
- 更新Todo场景:全局配置的
keepPreviousData: true会让React Query在查询失效重新获取时保留旧数据,调用invalidateQueries后,useQuery返回的仍是旧数据,打开模态框时传递的自然是编辑前的内容。 - setQueryData未生效:大概率是创建场景下
todoId还未更新为新Todo的ID,导致缓存更新的key错误;或getUpdatedTodo返回的数据结构与getTodo不一致,缓存更新后无法正确渲染。
针对性解决方案
1. 创建Todo时直接写入缓存并同步ID
创建成功后,立即将新Todo数据写入对应ID的缓存,并确保跳转时携带正确的新Todo ID,回到Todo页面后useQuery能直接读取缓存数据:
const todoMutation = useMutation( ['todo'], getUpdatedTodo, // 确保该函数返回包含id的完整新Todo对象 { onSuccess: (newTodo) => { // 用新Todo的ID作为缓存key写入数据 queryClient.setQueryData(['todo', newTodo.id], newTodo); // 跳转至新Todo详情页,传递正确ID navigate(`/todo/${newTodo.id}`); } } );
2. 更新Todo时优先同步缓存
更新成功后先通过setQueryData更新缓存,让useQuery立即返回最新数据,再按需失效查询确保后端数据同步:
const todoMutation = useMutation( ['todo'], getUpdatedTodo, // 返回更新后的完整Todo对象 { onSuccess: (updatedTodo) => { // 直接更新对应ID的缓存数据 queryClient.setQueryData(['todo', todoId], updatedTodo); // 可选:失效查询,确保后续请求拉取最新后端数据 queryClient.invalidateQueries(['todo', todoId]); } } );
3. 打开模态框时直接读取最新缓存
避免依赖useQuery返回的todo变量(可能存在更新延迟),直接从queryClient获取最新缓存数据传递给模态框:
const openModal = () => { // 直接从缓存中取最新的Todo数据 const latestTodo = queryClient.getQueryData(['todo', todoId]); modal.open(latestTodo); };
4. 按需关闭keepPreviousData
全局开启keepPreviousData可能不适用于Todo详情页场景,可针对单个查询关闭该配置,避免旧数据残留:
const useTodoQuery = () => { const {data, ...rest} = useQuery(['todo', todoId], async () => { if (todoId) { return await getTodo(todoId); } return null; }, { keepPreviousData: false // 仅当前查询关闭该选项 }); return {data, ...rest}; };
内容的提问来源于stack exchange,提问作者noer
相关产品推荐
相关产品推荐

