Skip to content

Commit c28c017

Browse files
committed
chore: move parameters out of zustand
1 parent 1117e98 commit c28c017

File tree

4 files changed

+89
-50
lines changed

4 files changed

+89
-50
lines changed

src/client/App.tsx

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,9 @@ import {
2929
type WasmLoadState,
3030
} from "@/utils/wasm";
3131
import { defaultCode } from "@/client/snippets";
32-
import type { PreviewOutput } from "@/gen/types";
32+
import type { ParameterWithSource, PreviewOutput } from "@/gen/types";
3333
import { useDebouncedValue } from "./hooks/debounce";
34+
import isEqual from "lodash/isEqual";
3435

3536
/**
3637
* Load the shared code if present.
@@ -67,6 +68,7 @@ export const App = () => {
6768
Record<string, string>
6869
>({});
6970
const [output, setOutput] = useState<PreviewOutput | null>(null);
71+
const [parameters, setParameters] = useState<ParameterWithSource[]>([]);
7072

7173
const onDownloadOutput = () => {
7274
const blob = new Blob([JSON.stringify(output, null, 2)], {
@@ -102,6 +104,23 @@ export const App = () => {
102104
}
103105
}, []);
104106

107+
useEffect(() => {
108+
setParameters((curr) => {
109+
const newParameters = output?.output?.parameters ?? [];
110+
111+
return newParameters.map((p) => {
112+
const existing = curr.find((currP) => {
113+
const currentParameterOmitValue = { ...currP, value: undefined };
114+
const existingParameterOmitValue = { ...p, value: undefined };
115+
116+
return isEqual(currentParameterOmitValue, existingParameterOmitValue);
117+
});
118+
119+
return existing ?? p;
120+
});
121+
});
122+
}, [output]);
123+
105124
useEffect(() => {
106125
getDynamicParametersOutput(debouncedCode, parameterValues)
107126
.catch((e) => {
@@ -171,6 +190,9 @@ export const App = () => {
171190
isDebouncing={isDebouncing}
172191
onDownloadOutput={onDownloadOutput}
173192
output={output}
193+
parameterValues={parameterValues}
194+
setParameterValues={setParameterValues}
195+
parameters={parameters}
174196
/>
175197
</ResizablePanelGroup>
176198
</main>

src/client/Preview.tsx

Lines changed: 64 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ import {
4242
XIcon,
4343
} from "lucide-react";
4444
import { AnimatePresence, motion } from "motion/react";
45+
import React from "react";
4546
import { type FC, type PropsWithChildren, useMemo, useState } from "react";
4647
import { useSearchParams } from "react-router";
4748

@@ -50,22 +51,26 @@ type PreviewProps = {
5051
isDebouncing: boolean;
5152
onDownloadOutput: () => void;
5253
output: PreviewOutput | null;
54+
parameterValues: Record<string, string>;
55+
setParameterValues: React.Dispatch<
56+
React.SetStateAction<Record<string, string>>
57+
>;
58+
parameters: ParameterWithSource[];
5359
};
5460

5561
export const Preview: FC<PreviewProps> = ({
5662
wasmLoadState,
5763
isDebouncing,
5864
output,
65+
parameterValues,
66+
setParameterValues,
67+
parameters,
5968
}) => {
6069
const $errors = useStore((state) => state.errors);
61-
const $resetForm = useStore((state) => state.resetForm);
6270
const [params] = useSearchParams();
63-
const isDebug = useMemo(() => params.has("debug"), [params]);
71+
const isDebug = params.has("debug");
6472
const [tab, setTab] = useState(() => "preview");
6573

66-
const parameters = output?.output?.parameters ?? [];
67-
console.info(parameters);
68-
6974
const onDownloadOutput = () => {
7075
const blob = new Blob([JSON.stringify(output, null, 2)], {
7176
type: "application/json",
@@ -238,11 +243,19 @@ export const Preview: FC<PreviewProps> = ({
238243
</div>
239244
) : (
240245
<div className="flex h-full w-full flex-col items-center justify-start gap-5 overflow-x-clip overflow-y-scroll rounded-xl border p-6">
241-
<Form parameters={parameters} />
246+
<Form
247+
parameters={parameters}
248+
parameterValues={parameterValues}
249+
setParameterValues={setParameterValues}
250+
/>
242251
</div>
243252
)}
244253
<div className="flex w-full justify-between gap-3">
245-
<Button variant="outline" onClick={$resetForm} className="w-fit">
254+
<Button
255+
variant="outline"
256+
onClick={() => setParameterValues({})}
257+
className="w-fit"
258+
>
246259
Reset form
247260
</Button>
248261
<ViewOutput parameters={parameters} />
@@ -518,50 +531,71 @@ const Log: FC<LogProps> = ({ log }) => {
518531
);
519532
};
520533

521-
type FormProps = { parameters: ParameterWithSource[] };
522-
523-
const Form: FC<FormProps> = ({ parameters }) => {
524-
const $force = useStore((state) => state._force);
525-
526-
const getParameterHash = (p: ParameterWithSource) =>
527-
`${$force}:${p.name}:${p.form_type}`;
534+
type FormProps = {
535+
parameters: ParameterWithSource[];
536+
parameterValues: Record<string, string>;
537+
setParameterValues: React.Dispatch<
538+
React.SetStateAction<Record<string, string>>
539+
>;
540+
};
528541

542+
const Form: FC<FormProps> = ({
543+
parameters,
544+
parameterValues,
545+
setParameterValues,
546+
}) => {
529547
return (
530548
parameters
531549
.sort((a, b) => a.order - b.order)
532550
// Since the form is sourced from constantly changing terraform, we are not sure
533551
// if the parameters are the "same" as the previous render.
534-
.map((p) => <FormElement key={getParameterHash(p)} parameter={p} />)
552+
.map((p) => {
553+
return (
554+
<FormElement
555+
key={p.name}
556+
parameter={p}
557+
value={parameterValues[p.name]}
558+
setParameterValues={setParameterValues}
559+
/>
560+
);
561+
})
535562
);
536563
};
537564

538-
type FormElementProps = { parameter: ParameterWithSource };
539-
const FormElement: FC<FormElementProps> = ({ parameter }) => {
540-
const $form = useStore((state) => state.form);
541-
const $setForm = useStore((state) => state.setFormState);
542-
543-
const value = useMemo(() => {
544-
const defaultValue =
545-
parameter.default_value.value !== "??"
546-
? parameter.default_value.value
547-
: undefined;
548-
return $form[parameter.name] ?? defaultValue;
549-
}, [$form, parameter.name, parameter.default_value]);
565+
type FormElementProps = {
566+
parameter: ParameterWithSource;
567+
value: string | undefined;
568+
setParameterValues: React.Dispatch<
569+
React.SetStateAction<Record<string, string>>
570+
>;
571+
};
572+
const FormElement: FC<FormElementProps> = React.memo(({
573+
parameter,
574+
value,
575+
setParameterValues,
576+
}) => {
577+
const defaultValue =
578+
parameter.default_value.value !== "??"
579+
? parameter.default_value.value
580+
: undefined;
550581

551582
const onValueChange = (value: string) => {
552-
$setForm(parameter.name, value);
583+
setParameterValues((curr) => {
584+
return { ...curr, [parameter.name]: value };
585+
});
553586
};
554587

555588
return (
556589
<DynamicParameter
557590
parameter={parameter}
558-
value={value}
591+
value={value ?? defaultValue}
559592
autofill={false}
560593
onChange={onValueChange}
561594
disabled={parameter.styling.disabled}
562595
/>
563596
);
564-
};
597+
});
598+
FormElement.displayName = "FormElement";
565599

566600
const UserSelect: FC = () => {
567601
const $setWorkspaceOwner = useStore((state) => state.setWorkspaceOwner);

src/client/store.tsx

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ import { create } from "zustand";
55
import { defaultCode } from "./snippets";
66
import { mockUsers } from "@/owner";
77

8-
export type FormState = Record<string, string>;
9-
108
type ErrorsState = {
119
diagnostics: Diagnostic[];
1210
show: boolean;
@@ -19,23 +17,19 @@ const defaultErrorsState: ErrorsState = {
1917
type State = {
2018
_force: number;
2119
editor: editor.IStandaloneCodeEditor | null;
22-
form: FormState;
2320
owner: WorkspaceOwner;
2421
errors: ErrorsState;
2522
setError: (diagnostics: Diagnostic[]) => void;
2623
toggleShowError: (open?: boolean) => void;
27-
setFormState: (key: string, value: string) => void;
2824
setEditor: (editor: editor.IStandaloneCodeEditor) => void;
2925
setWorkspaceOwner: (owner: WorkspaceOwner) => void;
30-
resetForm: () => void;
3126
};
3227

3328
export const useStore = create<State>()((set) => ({
3429
_force: 0,
3530
code: window.CODE ?? defaultCode,
3631
editor: null,
3732
owner: mockUsers.admin,
38-
form: {},
3933
errors: defaultErrorsState,
4034
setError: (data) =>
4135
set((state) => {
@@ -54,18 +48,6 @@ export const useStore = create<State>()((set) => ({
5448
},
5549
};
5650
}),
57-
setFormState: (key, value) =>
58-
set((state) => {
59-
const form = { ...state.form };
60-
form[key] = value;
61-
62-
return { form };
63-
}),
64-
resetForm: () =>
65-
set((state) => ({
66-
form: {},
67-
_force: state._force + 1,
68-
})),
6951
setEditor: (editor) => set(() => ({ editor })),
7052
setWorkspaceOwner: (owner) =>
7153
set((state) => ({

tsconfig.app.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@
2929
"noUnusedParameters": true,
3030
"erasableSyntaxOnly": true,
3131
"noFallthroughCasesInSwitch": true,
32-
"noUncheckedSideEffectImports": false
32+
"noUncheckedSideEffectImports": false,
33+
"noUncheckedIndexedAccess": true
3334
},
3435
"include": [
3536
"src"

0 commit comments

Comments
 (0)