Skip to content

Commit

Permalink
Avoid JSON parse error on playground load (astral-sh#6519)
Browse files Browse the repository at this point in the history
## Summary

On page load, the playground very briefly flickers a JSON parse error.
Due to our use of `useDeferredValue`, we attempt to parse the empty JSON
string settings, since after `const initialized = ruffVersion != null;`
returns true, we get one render with the stale deferred value.

This PR refactors the state, such that we start by storing `null` for
the `Source`, and use the `Source` itself to determine initialization
status.

## Test Plan

Set a breakpoint in the `catch` path in `Editor`; verified that it no
longer triggers on load (but did on `main`).
  • Loading branch information
charliermarsh authored Aug 12, 2023
1 parent c6ad364 commit a1da9da
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 34 deletions.
68 changes: 35 additions & 33 deletions playground/src/Editor/Editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,22 @@ import {
useMemo,
useState,
} from "react";
import { Panel, PanelGroup } from "react-resizable-panels";
import { DEFAULT_PYTHON_SOURCE } from "../constants";
import init, { Diagnostic, Workspace } from "../pkg";
import { ErrorMessage } from "./ErrorMessage";
import Header from "./Header";
import { useTheme } from "./theme";
import { persist, persistLocal, restore, stringify } from "./settings";
import SettingsEditor from "./SettingsEditor";
import SourceEditor from "./SourceEditor";
import { Panel, PanelGroup } from "react-resizable-panels";
import PrimarySideBar from "./PrimarySideBar";
import SecondarySideBar from "./SecondarySideBar";
import { HorizontalResizeHandle } from "./ResizeHandle";
import SecondaryPanel, {
SecondaryPanelResult,
SecondaryTool,
} from "./SecondaryPanel";
import SecondarySideBar from "./SecondarySideBar";
import { persist, persistLocal, restore, stringify } from "./settings";
import SettingsEditor from "./SettingsEditor";
import SourceEditor from "./SourceEditor";
import { useTheme } from "./theme";

type Tab = "Source" | "Settings";

Expand All @@ -43,11 +43,7 @@ export default function Editor() {
error: null,
secondary: null,
});
const [source, setSource] = useState<Source>({
pythonSource: "",
settingsSource: "",
revision: 0,
});
const [source, setSource] = useState<Source | null>(null);

const [tab, setTab] = useState<Tab>("Source");
const [secondaryTool, setSecondaryTool] = useState<SecondaryTool | null>(
Expand All @@ -64,8 +60,6 @@ export default function Editor() {
);
const [theme, setTheme] = useTheme();

const initialized = ruffVersion != null;

// Ideally this would be retrieved right from the URL... but routing without a proper
// router is hard (there's no location changed event) and pulling in a router
// feels overkill.
Expand Down Expand Up @@ -97,8 +91,8 @@ export default function Editor() {
];

setSource({
pythonSource,
revision: 0,
pythonSource,
settingsSource,
});
setRuffVersion(Workspace.version());
Expand All @@ -109,11 +103,11 @@ export default function Editor() {
const deferredSource = useDeferredValue(source);

useEffect(() => {
if (!initialized) {
if (deferredSource == null) {
return;
}

const { settingsSource, pythonSource } = deferredSource;
const { pythonSource, settingsSource } = deferredSource;

try {
const config = JSON.parse(settingsSource);
Expand Down Expand Up @@ -171,52 +165,60 @@ export default function Editor() {
secondary: null,
});
}
}, [initialized, deferredSource, secondaryTool]);
}, [deferredSource, secondaryTool]);

useEffect(() => {
if (initialized) {
if (source != null) {
persistLocal(source);
}
}, [initialized, source]);
}, [source]);

const handleShare = useMemo(() => {
if (!initialized) {
if (source == null) {
return undefined;
}

return () => {
return persist(source.settingsSource, source.pythonSource);
};
}, [source, initialized]);
}, [source]);

const handlePythonSourceChange = useCallback((pythonSource: string) => {
setSource((state) => ({
...state,
pythonSource,
revision: state.revision + 1,
}));
setSource((state) =>
state
? {
...state,
pythonSource,
revision: state.revision + 1,
}
: null,
);
}, []);

const handleSettingsSourceChange = useCallback((settingsSource: string) => {
setSource((state) => ({
...state,
settingsSource,
revision: state.revision + 1,
}));
setSource((state) =>
state
? {
...state,
settingsSource,
revision: state.revision + 1,
}
: null,
);
}, []);

return (
<main className="flex flex-col h-full bg-ayu-background dark:bg-ayu-background-dark">
<Header
edit={source.revision}
edit={source ? source.revision : null}
theme={theme}
version={ruffVersion}
onChangeTheme={setTheme}
onShare={handleShare}
/>

<div className="flex flex-grow">
{initialized ? (
{source ? (
<PanelGroup direction="horizontal" autoSaveId="main">
<PrimarySideBar
onSelectTool={(tool) => setTab(tool)}
Expand Down
2 changes: 1 addition & 1 deletion playground/src/Editor/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export default function Header({
onChangeTheme,
onShare,
}: {
edit: number;
edit: number | null;
theme: Theme;
version: string | null;
onChangeTheme: (theme: Theme) => void;
Expand Down

0 comments on commit a1da9da

Please sign in to comment.