mirror of
https://github.com/l0ng-ai/openidea.git
synced 2026-11-07 10:16:03 -10:00
fix(settings): keep staged object settings stable across renders (#293)
Settings › HTTP Proxy crashed ("Maximum update depth exceeded") when switching to No proxy or Manual. useSetting selected stagedValue() inside the zustand selector; for an object key ("proxy") whose child keys are staged separately ("proxy.mode", "proxy.host"), stagedValue builds a new merged object on every call, so the snapshot was never stable. useSetting now reuses useStagedValue, which selects base and staged separately and memoises the merge — covering every useSetting on an object-valued key. No other selector calls stagedValue.
Fixes #285
This commit is contained in:
1 parent
aeec0e6759
commit
c3b3534f08
2 files changed
+34
-2
No files matched your search
@@ -24,8 +24,9 @@ import { registerAction, executeAction } from "@/platform/actions";
|
||||
import { useProject } from "@/platform/project";
|
||||
import { useKeymap } from "@/platform/keymap";
|
||||
import { ContextMenuHost } from "@/ui";
|
||||
import { PageContext, useSetting } from "../components/context";
|
||||
import { DEFAULT_SETTINGS, setAt } from "../model/schema";
|
||||
import { stage, useSettingsDialog } from "../model/staging";
|
||||
import { openSettings, stage, useSettingsDialog } from "../model/staging";
|
||||
import { SettingsDialog } from "../dialog/SettingsDialog";
|
||||
import { activate } from "..";
|
||||
import { MessagesHost } from "@/workbench/messages";
|
||||
@@ -271,6 +272,37 @@ describe("Settings dialog", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("keeps object settings stable when child keys are staged (HTTP Proxy)", async () => {
|
||||
const seen: Array<{ mode: string; host: string }> = [];
|
||||
function Probe() {
|
||||
const [proxy] = useSetting<{ mode: string; host: string }>("proxy", "global");
|
||||
seen.push(proxy);
|
||||
return <output data-testid="proxy">{`${proxy.mode}:${proxy.host}`}</output>;
|
||||
}
|
||||
openSettings("general.httpProxy");
|
||||
const view = render(
|
||||
<PageContext.Provider value={{ pageId: "general.httpProxy", scope: "global" }}>
|
||||
<Probe />
|
||||
</PageContext.Provider>,
|
||||
);
|
||||
expect(screen.getByTestId("proxy")).toHaveTextContent("auto:");
|
||||
act(() => {
|
||||
stage("general.httpProxy", "global", "proxy.mode", "manual");
|
||||
stage("general.httpProxy", "global", "proxy.host", "127.0.0.1");
|
||||
});
|
||||
expect(screen.getByTestId("proxy")).toHaveTextContent("manual:127.0.0.1");
|
||||
expect(seen.at(-1)).toMatchObject({ mode: "manual", host: "127.0.0.1" });
|
||||
view.unmount();
|
||||
|
||||
const dialog = await openDialog("general.httpProxy");
|
||||
fireEvent.click(within(dialog).getByRole("radio", { name: "Manual proxy configuration" }));
|
||||
expect(within(dialog).getByRole("radio", { name: "Manual proxy configuration" })).toBeChecked();
|
||||
fireEvent.change(within(dialog).getByLabelText("Host name"), { target: { value: "127.0.0.1" } });
|
||||
expect(within(dialog).getByLabelText("Host name")).toHaveValue("127.0.0.1");
|
||||
fireEvent.click(within(dialog).getByRole("radio", { name: "No proxy" }));
|
||||
expect(within(dialog).getByRole("radio", { name: "No proxy" })).toBeChecked();
|
||||
});
|
||||
|
||||
it("survives invalid settings values", async () => {
|
||||
act(() =>
|
||||
useProject.setState({
|
||||
|
||||
@@ -19,7 +19,7 @@ export function usePageContext(): PageContextValue {
|
||||
export function useSetting<T>(key: string, scopeOverride?: Scope): [T, (value: T) => void] {
|
||||
const { pageId, scope: pageScope } = usePageContext();
|
||||
const scope = scopeOverride ?? pageScope;
|
||||
const value = useSettingsDialog((s) => stagedValue(s, scope, key)) as T;
|
||||
const value = useStagedValue(scope, key) as T;
|
||||
const set = useCallback((v: T) => stage(pageId, scope, key, v), [pageId, scope, key]);
|
||||
return [value, set];
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user