refactor(frontend): Simplify useConversation hook by removing context dependency (#8659)

Co-authored-by: openhands <openhands@all-hands.dev>
This commit is contained in:
sp.wack
2025-05-23 19:56:00 +04:00
committed by GitHub
parent efe287ce34
commit 20983a2128
13 changed files with 36 additions and 78 deletions

View File

@@ -4,7 +4,6 @@ import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { ActionSuggestions } from "#/components/features/chat/action-suggestions";
import OpenHands from "#/api/open-hands";
import { MOCK_DEFAULT_USER_SETTINGS } from "#/mocks/handlers";
import { ConversationProvider } from "#/context/conversation-context";
// Mock dependencies
vi.mock("posthog-js", () => ({
@@ -48,11 +47,9 @@ vi.mock("react-router", () => ({
const renderActionSuggestions = () =>
render(<ActionSuggestions onSuggestionsClick={() => {}} />, {
wrapper: ({ children }) => (
<ConversationProvider>
<QueryClientProvider client={new QueryClient()}>
{children}
</QueryClientProvider>
</ConversationProvider>
<QueryClientProvider client={new QueryClient()}>
{children}
</QueryClientProvider>
),
});

View File

@@ -3,7 +3,7 @@ import { useSelector, useDispatch } from "react-redux";
import { RootState } from "#/store";
import { BrowserSnapshot } from "./browser-snapshot";
import { EmptyBrowserMessage } from "./empty-browser-message";
import { useConversation } from "#/context/conversation-context";
import { useConversationId } from "#/hooks/use-conversation-id";
import {
initialState as browserInitialState,
setUrl,
@@ -14,7 +14,7 @@ export function BrowserPanel() {
const { url, screenshotSrc } = useSelector(
(state: RootState) => state.browser,
);
const { conversationId } = useConversation();
const { conversationId } = useConversationId();
const dispatch = useDispatch();
useEffect(() => {

View File

@@ -4,7 +4,7 @@ import { useTranslation } from "react-i18next";
import { SuggestionItem } from "#/components/features/suggestions/suggestion-item";
import { I18nKey } from "#/i18n/declaration";
import { useUserProviders } from "#/hooks/use-user-providers";
import { useConversation } from "#/context/conversation-context";
import { useConversationId } from "#/hooks/use-conversation-id";
import { useUserConversation } from "#/hooks/query/use-user-conversation";
interface ActionSuggestionsProps {
@@ -16,7 +16,7 @@ export function ActionSuggestions({
}: ActionSuggestionsProps) {
const { t } = useTranslation();
const { providers } = useUserProviders();
const { conversationId } = useConversation();
const { conversationId } = useConversationId();
const { data: conversation } = useUserConversation(conversationId);
const [hasPullRequest, setHasPullRequest] = React.useState(false);

View File

@@ -1,42 +0,0 @@
import React, { useMemo } from "react";
import { useParams } from "react-router";
interface ConversationContextType {
conversationId: string;
}
const ConversationContext = React.createContext<ConversationContextType | null>(
null,
);
export function ConversationProvider({
children,
}: {
children: React.ReactNode;
}) {
const { conversationId } = useParams<{ conversationId: string }>();
if (!conversationId) {
throw new Error(
"ConversationProvider must be used within a route that has a conversationId parameter",
);
}
const value = useMemo(() => ({ conversationId }), [conversationId]);
return (
<ConversationContext.Provider value={value}>
{children}
</ConversationContext.Provider>
);
}
export function useConversation() {
const context = React.useContext(ConversationContext);
if (!context) {
throw new Error(
"useConversation must be used within a ConversationProvider",
);
}
return context;
}

View File

@@ -1,7 +1,7 @@
import { useMutation } from "@tanstack/react-query";
import { Feedback } from "#/api/open-hands.types";
import OpenHands from "#/api/open-hands";
import { useConversation } from "#/context/conversation-context";
import { useConversationId } from "#/hooks/use-conversation-id";
import { displayErrorToast } from "#/utils/custom-toast-handlers";
type SubmitFeedbackArgs = {
@@ -9,7 +9,7 @@ type SubmitFeedbackArgs = {
};
export const useSubmitFeedback = () => {
const { conversationId } = useConversation();
const { conversationId } = useConversationId();
return useMutation({
mutationFn: ({ feedback }: SubmitFeedbackArgs) =>
OpenHands.submitFeedback(conversationId, feedback),

View File

@@ -5,13 +5,13 @@ import { useSelector } from "react-redux";
import OpenHands from "#/api/open-hands";
import { RUNTIME_INACTIVE_STATES } from "#/types/agent-state";
import { RootState } from "#/store";
import { useConversation } from "#/context/conversation-context";
import { useConversationId } from "#/hooks/use-conversation-id";
export const useActiveHost = () => {
const { curAgentState } = useSelector((state: RootState) => state.agent);
const [activeHost, setActiveHost] = React.useState<string | null>(null);
const { conversationId } = useConversation();
const { conversationId } = useConversationId();
const { data } = useQuery({
queryKey: [conversationId, "hosts"],

View File

@@ -4,12 +4,12 @@ import {
useWsClient,
WsClientProviderStatus,
} from "#/context/ws-client-provider";
import { useConversation } from "#/context/conversation-context";
import { useConversationId } from "#/hooks/use-conversation-id";
import OpenHands from "#/api/open-hands";
export const useConversationConfig = () => {
const { status } = useWsClient();
const { conversationId } = useConversation();
const { conversationId } = useConversationId();
const query = useQuery({
queryKey: ["conversation_config", conversationId],

View File

@@ -1,7 +1,7 @@
import { useQuery } from "@tanstack/react-query";
import OpenHands from "#/api/open-hands";
import { GitChangeStatus } from "#/api/open-hands.types";
import { useConversation } from "#/context/conversation-context";
import { useConversationId } from "#/hooks/use-conversation-id";
type UseGetDiffConfig = {
filePath: string;
@@ -10,7 +10,7 @@ type UseGetDiffConfig = {
};
export const useGitDiff = (config: UseGetDiffConfig) => {
const { conversationId } = useConversation();
const { conversationId } = useConversationId();
return useQuery({
queryKey: ["file_diff", conversationId, config.filePath, config.type],

View File

@@ -2,13 +2,13 @@ import { useQuery } from "@tanstack/react-query";
import React from "react";
import { useSelector } from "react-redux";
import OpenHands from "#/api/open-hands";
import { useConversation } from "#/context/conversation-context";
import { useConversationId } from "#/hooks/use-conversation-id";
import { GitChange } from "#/api/open-hands.types";
import { RootState } from "#/store";
import { RUNTIME_INACTIVE_STATES } from "#/types/agent-state";
export const useGetGitChanges = () => {
const { conversationId } = useConversation();
const { conversationId } = useConversationId();
const [orderedChanges, setOrderedChanges] = React.useState<GitChange[]>([]);
const previousDataRef = React.useRef<GitChange[]>(null);

View File

@@ -2,7 +2,7 @@ import { useQuery } from "@tanstack/react-query";
import { useTranslation } from "react-i18next";
import { useSelector } from "react-redux";
import OpenHands from "#/api/open-hands";
import { useConversation } from "#/context/conversation-context";
import { useConversationId } from "#/hooks/use-conversation-id";
import { I18nKey } from "#/i18n/declaration";
import { RootState } from "#/store";
import { RUNTIME_INACTIVE_STATES } from "#/types/agent-state";
@@ -16,7 +16,7 @@ interface VSCodeUrlResult {
export const useVSCodeUrl = () => {
const { t } = useTranslation();
const { conversationId } = useConversation();
const { conversationId } = useConversationId();
const { curAgentState } = useSelector((state: RootState) => state.agent);
const isRuntimeInactive = RUNTIME_INACTIVE_STATES.includes(curAgentState);

View File

@@ -0,0 +1,13 @@
import { useParams } from "react-router";
export function useConversationId() {
const { conversationId } = useParams<{ conversationId: string }>();
if (!conversationId) {
throw new Error(
"useConversationId must be used within a route that has a conversationId parameter",
);
}
return { conversationId };
}

View File

@@ -8,10 +8,7 @@ import { DiGit } from "react-icons/di";
import { VscCode } from "react-icons/vsc";
import { I18nKey } from "#/i18n/declaration";
import { RUNTIME_INACTIVE_STATES } from "#/types/agent-state";
import {
ConversationProvider,
useConversation,
} from "#/context/conversation-context";
import { useConversationId } from "#/hooks/use-conversation-id";
import { Controls } from "#/components/features/controls/controls";
import { clearTerminal } from "#/state/command-slice";
import { useEffectOnce } from "#/hooks/use-effect-once";
@@ -44,7 +41,7 @@ function AppContent() {
useConversationConfig();
const { t } = useTranslation();
const { data: settings } = useSettings();
const { conversationId } = useConversation();
const { conversationId } = useConversationId();
const { data: conversation, isFetched } = useUserConversation(
conversationId || null,
);
@@ -213,11 +210,7 @@ function AppContent() {
}
function App() {
return (
<ConversationProvider>
<AppContent />
</ConversationProvider>
);
return <AppContent />;
}
export default App;

View File

@@ -10,7 +10,6 @@ import i18n from "i18next";
import { vi } from "vitest";
import { AxiosError } from "axios";
import { AppStore, RootState, rootReducer } from "./src/store";
import { ConversationProvider } from "#/context/conversation-context";
// Mock useParams before importing components
vi.mock("react-router", async () => {
@@ -72,9 +71,7 @@ export function renderWithProviders(
})
}
>
<ConversationProvider>
<I18nextProvider i18n={i18n}>{children}</I18nextProvider>
</ConversationProvider>
<I18nextProvider i18n={i18n}>{children}</I18nextProvider>
</QueryClientProvider>
</Provider>
);