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

如何在React Query多次重取数据时保留历史有效数据?

问题分析

你的核心问题出在两处:

  1. 轮询未成功时,useQuery的queryFn返回了undefined,直接把查询的data字段置为undefined,覆盖了之前的成功数据;
  2. 输入文本变化时,查询key中的text更新触发了新查询,即便开启了keepPreviousData,新查询返回的undefined最终还是会替换旧的成功数据。

下面提供两种直接可行的解决方案:


方案一:单独维护最后成功的数据状态

在Hook内部新增状态存储最后一次成功返回的数据,让组件优先展示这个状态,完全忽略轮询过程中的无效数据。

修改后的完整代码:

export function useSuggestions(
  text: string,
  id: string,
): { data: TaskTextStatus | undefined; isLoading: boolean; isFetching: boolean } {
  const [task, setTask] = useState<string>();
  const [stopRefetch, setStopRefetch] = useState(false);
  // 新增:专门存储最后一次成功的任务数据
  const [lastSuccessData, setLastSuccessData] = useState<TaskTextStatus>();
  const [textDebounced] = useDebounce(text, 1000);

  useEffect(() => {
    mutate(text);
  }, [textDebounced]);

  const { mutate } = useMutation(
    ["suggestions", id, text],
    async (text: string) => {
      if (!text) return;
      const res = await sendToBackground<{ text: string }, TaskTextSubmit>({
        name: "send-text",
        body: { text },
      });
      return res;
    },
    {
      onSuccess(data) {
        if (!data) return;
        setTask(data.task_uuid);
      },
      onError(e) {
        console.log(e, "ERROR");
      },
    },
  );

  const queryResult = useQuery(
    // 调整查询key:用task_uuid替代text,只有新任务创建时才启动新查询
    ["suggestions", id, task],
    async () => {
      if (!task) throw new Error("无任务UUID");
      setStopRefetch(false);
      const res = await sendToBackground<{ task_uuid: string }, TaskTextStatus>({
        name: "check-task",
        body: { task_uuid: task },
      });

      if (res.is_success) {
        return res;
      } else {
        // 未成功时抛出错误,避免返回undefined覆盖数据
        throw new Error("任务未完成");
      }
    },
    {
      enabled: !!task,
      refetchInterval: stopRefetch ? false : 2000,
      keepPreviousData: true,
      refetchIntervalInBackground: true,
      refetchOnWindowFocus: false,
      onSuccess(data) {
        if (data?.is_success) {
          // 仅在任务成功时更新最后成功数据
          setLastSuccessData(data);
          setStopRefetch(true);
          setTask("");
        }
      },
      onError(err) {
        // 只有非任务pending的错误才停止轮询
        if (err.message !== "任务未完成") {
          setStopRefetch(true);
        }
      },
    },
  );

  // 返回最后成功数据和查询状态,供组件使用
  return {
    data: lastSuccessData,
    // 只有首次加载且无历史数据时才显示加载状态
    isLoading: queryResult.isLoading && !lastSuccessData,
    isFetching: queryResult.isFetching,
  };
}

方案二:利用React Query错误机制保留旧数据

React Query在查询抛出错误时,不会更新data字段,会自动保留之前的成功数据。只要让未成功的轮询抛出错误,就能实现需求。

修改核心部分代码:

// ... 其他代码保持不变
const queryResult = useQuery(
  ["suggestions", id, text],
  async () => {
    if (!task) throw new Error("无任务可用");
    setStopRefetch(false);
    const res = await sendToBackground<{ task_uuid: string }, TaskTextStatus>({
      name: "check-task",
      body: { task_uuid: task },
    });

    // 未成功时抛出错误,阻止data被更新
    if (!res.is_success) {
      throw new Error("任务处理中");
    }
    return res;
  },
  {
    enabled: !!task,
    refetchInterval: stopRefetch ? false : 2000,
    keepPreviousData: true,
    refetchIntervalInBackground: true,
    refetchOnWindowFocus: false,
    onSuccess(data) {
      if (data?.is_success) {
        setStopRefetch(true);
        setTask("");
      }
    },
    onError(err) {
      // 仅在非任务pending的错误时停止轮询
      if (err.message !== "任务处理中") {
        setStopRefetch(true);
      }
    },
  },
);

return queryResult;

使用时,直接用queryResult.data即可,它会始终保留最后一次成功返回的数据,直到新的成功数据到来。


内容的提问来源于stack exchange,提问作者Devkey

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 02:20:56