You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React Query竞态条件问题:Todo应用模态框显示旧数据

React Query Todo应用竞态条件问题解决方案

问题根源分析

  1. 创建Todo场景:新Todo创建成功后跳转回Todo页面,对应新Todo的查询缓存尚未建立,打开模态框时才触发查询,首次获取数据前模态框拿到的是空值。
  2. 更新Todo场景:全局配置的keepPreviousData: true会让React Query在查询失效重新获取时保留旧数据,调用invalidateQueries后,useQuery返回的仍是旧数据,打开模态框时传递的自然是编辑前的内容。
  3. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 19:04:51