Unverified 提交 a4747a49 authored 作者: Will Chen's avatar Will Chen 提交者: GitHub

Use idiomatic react-query for useProposal (#2018)

<!-- CURSOR_SUMMARY --> > [!NOTE] > **Refactor: proposal fetching via react-query** > > - Replaces Jotai state/effects with `useQuery` in `useProposal` (`queryKey: ["proposal", chatId]`, `enabled` gating, `refetch` exposed, error typed as `Error`) > - Deletes `src/atoms/proposalAtoms.ts`; `proposalResultAtom` removed > - In `useStreamChat`, stop using `useProposal`; on stream end, call `queryClient.invalidateQueries({ queryKey: ["proposal", chatId] })` > - In `ChatInput`, display `proposalError.message` and keep using `refreshProposal()` from the hook > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 9739b1befe76a7cb491594815d3d92977ad6a1c6. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY --> <!-- This is an auto-generated description by cubic. --> --- ## Summary by cubic Refactored proposal fetching to use React Query’s useQuery, removing Jotai state and simplifying refresh and error handling. Improves reliability and makes the hook easier to use. - **Refactors** - Replaced custom state/effects with useQuery (key: ["proposal", chatId], enabled only when chatId). - Removed proposalAtoms and Jotai; hook now returns proposalResult, isLoading, error, refreshProposal. - Updated useStreamChat to invalidate the proposal query via QueryClient (invalidateQueries(["proposal", chatId])). - ChatInput now displays proposalError.message for clearer errors. - Added meta: { showErrorToast: true } to surface fetch errors. <sup>Written for commit 9739b1befe76a7cb491594815d3d92977ad6a1c6. Summary will update automatically on new commits.</sup> <!-- End of auto-generated description by cubic. -->
上级 04087298
import { atom } from "jotai";
import type { ProposalResult } from "@/lib/schemas";
export const proposalResultAtom = atom<ProposalResult | null>(null);
...@@ -305,7 +305,7 @@ export function ChatInput({ chatId }: { chatId?: number }) { ...@@ -305,7 +305,7 @@ export function ChatInput({ chatId }: { chatId?: number }) {
)} )}
{proposalError && ( {proposalError && (
<div className="p-4 text-sm text-red-600"> <div className="p-4 text-sm text-red-600">
Error loading proposal: {proposalError} Error loading proposal: {proposalError.message}
</div> </div>
)} )}
<div className="p-4" data-testid="chat-input-container"> <div className="p-4" data-testid="chat-input-container">
......
import { useState, useEffect, useCallback } from "react"; import { useQuery } from "@tanstack/react-query";
import { IpcClient } from "@/ipc/ipc_client"; import { IpcClient } from "@/ipc/ipc_client";
import type { ProposalResult } from "@/lib/schemas"; // Import Proposal type import type { ProposalResult } from "@/lib/schemas";
import { proposalResultAtom } from "@/atoms/proposalAtoms";
import { useAtom } from "jotai";
export function useProposal(chatId?: number | undefined) { export function useProposal(chatId?: number | undefined) {
const [proposalResult, setProposalResult] = useAtom(proposalResultAtom); const {
const [isLoading, setIsLoading] = useState<boolean>(false); data: proposalResult,
const [error, setError] = useState<string | null>(null); isLoading,
const fetchProposal = useCallback( error,
async (overrideChatId?: number) => { refetch: refreshProposal,
chatId = overrideChatId ?? chatId; } = useQuery<ProposalResult | null, Error>({
queryKey: ["proposal", chatId],
queryFn: async (): Promise<ProposalResult | null> => {
if (chatId === undefined) { if (chatId === undefined) {
setProposalResult(null); return null;
setIsLoading(false);
setError(null);
return;
}
setIsLoading(true);
setError(null);
try {
// Type assertion might be needed depending on how IpcClient is typed
const result = (await IpcClient.getInstance().getProposal(
chatId,
)) as ProposalResult | null;
if (result) {
setProposalResult(result);
} else {
setProposalResult(null); // Explicitly set to null if IPC returns null
}
} catch (err: any) {
console.error("Error fetching proposal:", err);
setError(err.message || "Failed to fetch proposal");
setProposalResult(null); // Clear proposal data on error
} finally {
setIsLoading(false);
} }
return IpcClient.getInstance().getProposal(chatId);
}, },
[chatId], // Only depend on chatId, setProposalResult is stable enabled: chatId !== undefined,
); // Depend on chatId meta: { showErrorToast: true },
});
useEffect(() => {
fetchProposal();
// Cleanup function if needed (e.g., for aborting requests)
// return () => {
// // Abort logic here
// };
}, [fetchProposal]); // Re-run effect if fetchProposal changes (due to chatId change)
return { return {
proposalResult: proposalResult, proposalResult,
isLoading, isLoading,
error, error,
refreshProposal: fetchProposal, // Expose the refresh function refreshProposal,
}; };
} }
...@@ -20,7 +20,6 @@ import { useLoadApp } from "./useLoadApp"; ...@@ -20,7 +20,6 @@ import { useLoadApp } from "./useLoadApp";
import { selectedAppIdAtom } from "@/atoms/appAtoms"; import { selectedAppIdAtom } from "@/atoms/appAtoms";
import { useVersions } from "./useVersions"; import { useVersions } from "./useVersions";
import { showExtraFilesToast } from "@/lib/toast"; import { showExtraFilesToast } from "@/lib/toast";
import { useProposal } from "./useProposal";
import { useSearch } from "@tanstack/react-router"; import { useSearch } from "@tanstack/react-router";
import { useRunApp } from "./useRunApp"; import { useRunApp } from "./useRunApp";
import { useCountTokens } from "./useCountTokens"; import { useCountTokens } from "./useCountTokens";
...@@ -28,6 +27,7 @@ import { useUserBudgetInfo } from "./useUserBudgetInfo"; ...@@ -28,6 +27,7 @@ import { useUserBudgetInfo } from "./useUserBudgetInfo";
import { usePostHog } from "posthog-js/react"; import { usePostHog } from "posthog-js/react";
import { useCheckProblems } from "./useCheckProblems"; import { useCheckProblems } from "./useCheckProblems";
import { useSettings } from "./useSettings"; import { useSettings } from "./useSettings";
import { useQueryClient } from "@tanstack/react-query";
export function getRandomNumberId() { export function getRandomNumberId() {
return Math.floor(Math.random() * 1_000_000_000_000_000); return Math.floor(Math.random() * 1_000_000_000_000_000);
...@@ -54,13 +54,13 @@ export function useStreamChat({ ...@@ -54,13 +54,13 @@ export function useStreamChat({
const { settings } = useSettings(); const { settings } = useSettings();
const setRecentStreamChatIds = useSetAtom(recentStreamChatIdsAtom); const setRecentStreamChatIds = useSetAtom(recentStreamChatIdsAtom);
const posthog = usePostHog(); const posthog = usePostHog();
const queryClient = useQueryClient();
let chatId: number | undefined; let chatId: number | undefined;
if (hasChatId) { if (hasChatId) {
const { id } = useSearch({ from: "/chat" }); const { id } = useSearch({ from: "/chat" });
chatId = id; chatId = id;
} }
let { refreshProposal } = hasChatId ? useProposal(chatId) : useProposal();
const { invalidateTokenCount } = useCountTokens(chatId ?? null, ""); const { invalidateTokenCount } = useCountTokens(chatId ?? null, "");
const streamMessage = useCallback( const streamMessage = useCallback(
...@@ -141,7 +141,8 @@ export function useStreamChat({ ...@@ -141,7 +141,8 @@ export function useStreamChat({
posthog, posthog,
}); });
} }
refreshProposal(chatId); // Use queryClient directly with the chatId parameter to avoid stale closure issues
queryClient.invalidateQueries({ queryKey: ["proposal", chatId] });
refetchUserBudget(); refetchUserBudget();
...@@ -205,6 +206,7 @@ export function useStreamChat({ ...@@ -205,6 +206,7 @@ export function useStreamChat({
selectedAppId, selectedAppId,
refetchUserBudget, refetchUserBudget,
settings, settings,
queryClient,
], ],
); );
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论