Skip to content

Commit 4412f19

Browse files
authored
fix: sync websocket params with form params (#17895)
The current issue is that when multiple parameters are added or removed from a form because a user change in a conditional parameter value. The websocket parameters response gets out of sync with the state of the parameters in the form. The form state needs to be maintained because this is what gets submitted when the user attempts to create a workspace. Fixes: 1. When autofill params are set from the url, mark these params as touched in the form. This is necessary as only touched params are sent in the request to the websocket. These params should technically count as being touched because they were preset from the url params. 2. Create a hook to synchronize the parameters from the websocket response with the current state of the parameters stored in the form.
1 parent 766277c commit 4412f19

File tree

1 file changed

+77
-4
lines changed

1 file changed

+77
-4
lines changed

site/src/pages/CreateWorkspacePage/CreateWorkspacePageViewExperimental.tsx

Lines changed: 77 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,27 @@ export const CreateWorkspacePageViewExperimental: FC<
113113
setSuggestedName(() => generateWorkspaceName());
114114
}, []);
115115

116+
const autofillByName = Object.fromEntries(
117+
autofillParameters.map((param) => [param.name, param]),
118+
);
119+
120+
// Only touched fields are sent to the websocket
121+
// Autofilled parameters are marked as touched since they have been modified
122+
const initialTouched = parameters.reduce(
123+
(touched, parameter) => {
124+
if (autofillByName[parameter.name] !== undefined) {
125+
touched[parameter.name] = true;
126+
}
127+
return touched;
128+
},
129+
{} as Record<string, boolean>,
130+
);
131+
132+
// The form parameters values hold the working state of the parameters that will be submitted when creating a workspace
133+
// 1. The form parameter values are initialized from the websocket response when the form is mounted
134+
// 2. Only touched form fields are sent to the websocket, a field is touched if edited by the user or set by autofill
135+
// 3. The websocket response may add or remove parameters, these are added or removed from the form values in the useSyncFormParameters hook
136+
// 4. All existing form parameters are updated to match the websocket response in the useSyncFormParameters hook
116137
const form: FormikContextType<TypesGen.CreateWorkspaceRequest> =
117138
useFormik<TypesGen.CreateWorkspaceRequest>({
118139
initialValues: {
@@ -123,6 +144,7 @@ export const CreateWorkspacePageViewExperimental: FC<
123144
autofillParameters,
124145
),
125146
},
147+
initialTouched,
126148
validationSchema: Yup.object({
127149
name: nameValidator("Workspace Name"),
128150
rich_parameter_values:
@@ -140,10 +162,6 @@ export const CreateWorkspacePageViewExperimental: FC<
140162
},
141163
});
142164

143-
const autofillByName = Object.fromEntries(
144-
autofillParameters.map((param) => [param.name, param]),
145-
);
146-
147165
useEffect(() => {
148166
if (error) {
149167
window.scrollTo(0, 0);
@@ -250,6 +268,12 @@ export const CreateWorkspacePageViewExperimental: FC<
250268
sendDynamicParamsRequest(parameter, value);
251269
};
252270

271+
useSyncFormParameters({
272+
parameters,
273+
formValues: form.values.rich_parameter_values ?? [],
274+
setFieldValue: form.setFieldValue,
275+
});
276+
253277
return (
254278
<>
255279
<div className="sticky top-5 ml-10">
@@ -579,3 +603,52 @@ const Diagnostics: FC<DiagnosticsProps> = ({ diagnostics }) => {
579603
</div>
580604
);
581605
};
606+
607+
type UseSyncFormParametersProps = {
608+
parameters: readonly PreviewParameter[];
609+
formValues: readonly TypesGen.WorkspaceBuildParameter[];
610+
setFieldValue: (
611+
field: string,
612+
value: TypesGen.WorkspaceBuildParameter[],
613+
) => void;
614+
};
615+
616+
function useSyncFormParameters({
617+
parameters,
618+
formValues,
619+
setFieldValue,
620+
}: UseSyncFormParametersProps) {
621+
// Form values only needs to be updated when parameters change
622+
// Keep track of form values in a ref to avoid unnecessary updates to rich_parameter_values
623+
const formValuesRef = useRef(formValues);
624+
625+
useEffect(() => {
626+
formValuesRef.current = formValues;
627+
}, [formValues]);
628+
629+
useEffect(() => {
630+
if (!parameters) return;
631+
const currentFormValues = formValuesRef.current;
632+
633+
const newParameterValues = parameters.map((param) => {
634+
return {
635+
name: param.name,
636+
value: param.value.valid ? param.value.value : "",
637+
};
638+
});
639+
640+
const isChanged =
641+
currentFormValues.length !== newParameterValues.length ||
642+
newParameterValues.some(
643+
(p) =>
644+
!currentFormValues.find(
645+
(formValue) =>
646+
formValue.name === p.name && formValue.value === p.value,
647+
),
648+
);
649+
650+
if (isChanged) {
651+
setFieldValue("rich_parameter_values", newParameterValues);
652+
}
653+
}, [parameters, setFieldValue]);
654+
}

0 commit comments

Comments
 (0)