Skip to content

Commit 8c1295f

Browse files
author
Ruben van Leeuwen
committed
Baseline working setup
1 parent 26905ce commit 8c1295f

File tree

1 file changed

+45
-100
lines changed

1 file changed

+45
-100
lines changed

frontend/packages/pydantic-forms/src/core/PydanticFormContextProvider.tsx

Lines changed: 45 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import {
3434
import {
3535
Locale,
3636
PydanticFormContextProps,
37+
PydanticFormFieldType,
3738
PydanticFormInitialContextProps,
3839
PydanticFormSchemaRawJson,
3940
PydanticFormValidationErrorDetails,
@@ -71,6 +72,11 @@ function PydanticFormContextProvider({
7172
cancelButton,
7273
} = config;
7374

75+
const awaitReset = async (payLoad: FieldValues = {}) => {
76+
rhf.reset(payLoad);
77+
await new Promise((resolve) => setTimeout(resolve, 0)); // wait one tick
78+
};
79+
7480
const [formInputHistory, setFormInputHistory] = useState(
7581
new Map<string, object>(),
7682
);
@@ -95,19 +101,6 @@ function PydanticFormContextProvider({
95101
});
96102
};
97103

98-
const addFormInputData = useCallback(
99-
(formInput: object, replaceInsteadOfAdd = false) => {
100-
setFormInputData((currentInputs) => {
101-
const data = replaceInsteadOfAdd
102-
? currentInputs.slice(0, -1)
103-
: currentInputs;
104-
updateHistory(formInput, data);
105-
return [...data, formInput];
106-
});
107-
},
108-
[],
109-
);
110-
111104
const [errorDetails, setErrorDetails] =
112105
useState<PydanticFormValidationErrorDetails>();
113106
const [isFullFilled, setIsFullFilled] = useState(false);
@@ -129,17 +122,17 @@ function PydanticFormContextProvider({
129122
error,
130123
} = useApiProvider(formKey, formInputData, apiProvider, metaData);
131124

132-
const [rawSchema, setRawSchema] = useState<PydanticFormSchemaRawJson>();
125+
const emptyRawSchema: PydanticFormSchemaRawJson = {
126+
type: PydanticFormFieldType.OBJECT,
127+
properties: {},
128+
};
129+
const [rawSchema, setRawSchema] =
130+
useState<PydanticFormSchemaRawJson>(emptyRawSchema);
133131
const [hasNext, setHasNext] = useState<boolean>(false);
134132

135133
// extract the JSON schema to a more usable custom schema
136134
const { pydanticFormSchema, isLoading: isParsingSchema } =
137-
usePydanticFormParser(
138-
rawSchema,
139-
formLabels?.labels,
140-
fieldDetailProvider,
141-
formStructureMutator,
142-
);
135+
usePydanticFormParser(rawSchema, formKey, formLabels?.labels);
143136

144137
// build validation rules based on custom schema
145138
const zodSchema = useGetZodValidator(
@@ -169,17 +162,31 @@ function PydanticFormContextProvider({
169162
mode: 'all',
170163
defaultValues: initialData,
171164
values: initialData,
172-
shouldUnregister: true,
173165
});
174166

167+
const addFormInputData = useCallback(
168+
(formInput: object, replaceInsteadOfAdd = false) => {
169+
setFormInputData((currentInputs) => {
170+
const data = replaceInsteadOfAdd
171+
? currentInputs.slice(0, -1)
172+
: currentInputs;
173+
updateHistory(formInput, data);
174+
return [...data, { ...formInput }];
175+
});
176+
awaitReset();
177+
},
178+
[awaitReset, setFormInputData, updateHistory],
179+
);
180+
175181
const submitFormFn = useCallback(() => {
176182
setIsSending(true);
177-
const rhfValues = rhf.getValues();
183+
const rhfValues = _.cloneDeep(rhf.getValues());
184+
awaitReset();
178185
// Note. If we don't use cloneDeep here we are adding a reference to the rhfValues
179186
// that changes on every change in the form and triggering effects before we want to.
180-
addFormInputData(_.cloneDeep(rhfValues), !!errorDetails);
187+
addFormInputData(rhfValues, !!errorDetails);
181188
window.scrollTo(0, 0);
182-
}, [rhf, errorDetails, addFormInputData]);
189+
}, [rhf, errorDetails, addFormInputData, awaitReset, setIsSending]);
183190

184191
const onClientSideError = useCallback(
185192
(data?: FieldValues) => {
@@ -198,7 +205,7 @@ function PydanticFormContextProvider({
198205
(e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
199206
e.preventDefault();
200207
setErrorDetails(undefined);
201-
rhf.reset();
208+
awaitReset();
202209
rhf.trigger();
203210
},
204211
[rhf],
@@ -217,7 +224,7 @@ function PydanticFormContextProvider({
217224
const clearForm = useCallback(() => {
218225
setFormInputData([]);
219226
setIsFullFilled(false);
220-
setRawSchema(undefined);
227+
setRawSchema(emptyRawSchema);
221228
setHasNext(false);
222229
}, []);
223230

@@ -253,40 +260,23 @@ function PydanticFormContextProvider({
253260
initialData,
254261
};
255262

256-
/** UseEffects */
257-
useEffect(() => {
258-
if (formKey !== formRef.current) {
259-
// When the formKey changes we need to reset the form input data
260-
setFormInputData([]);
261-
setFormInputHistory(new Map<string, object>());
262-
formRef.current = formKey;
263-
}
264-
}, [formKey]);
265-
266-
// handle successfull submits
263+
// a useeffect for whenever the error response updates
264+
// sometimes we need to update the form,
265+
// some we need to update the errors
267266
useEffect(() => {
268-
if (!isFullFilled) {
267+
if (!apiResponse) {
269268
return;
270269
}
271-
272-
if (onSuccess) {
273-
const values = rhf.getValues();
274-
if (skipSuccessNotice) {
275-
onSuccess(values, apiResponse || {});
276-
} else {
277-
setTimeout(() => {
278-
onSuccess?.(values, apiResponse || {});
279-
}, 1500); // Delay to allow notice to show first
280-
}
270+
// when we receive errors, we append to the scheme
271+
if (apiResponse?.validation_errors) {
272+
// Restore the data we got the error with
273+
const errorPayload = [...formInputData].pop();
274+
awaitReset(errorPayload);
275+
setErrorDetails(getErrorDetailsFromResponse(apiResponse));
276+
return;
281277
}
282278

283-
setFormInputHistory(new Map<string, object>());
284-
}, [apiResponse, isFullFilled, onSuccess, rhf, skipSuccessNotice]);
285-
286-
// a useeffect for whenever the error response updates
287-
// sometimes we need to update the form,
288-
// some we need to update the errors
289-
useEffect(() => {
279+
awaitReset();
290280
if (apiResponse?.success) {
291281
setIsFullFilled(true);
292282
return;
@@ -301,55 +291,10 @@ function PydanticFormContextProvider({
301291
setErrorDetails(undefined);
302292
}
303293

304-
// when we receive errors, we append to the scheme
305-
if (apiResponse?.validation_errors) {
306-
setErrorDetails(getErrorDetailsFromResponse(apiResponse));
307-
}
308-
309294
setIsSending(false);
310295
// eslint-disable-next-line react-hooks/exhaustive-deps
311296
}, [apiResponse]); // Avoid completing the dependencies array here to avoid unwanted resetFormData calls
312297
// a useeffect for filling data whenever formdefinition or labels update
313-
314-
useEffect(() => {
315-
getHashForArray(formInputData).then((hash) => {
316-
const currentStepFromHistory = formInputHistory.get(hash);
317-
318-
if (currentStepFromHistory) {
319-
rhf.reset(currentStepFromHistory);
320-
}
321-
});
322-
}, [formInputData, formInputHistory, rhf]);
323-
324-
// this is to show an error whenever there is an unexpected error from the backend
325-
// for instance a 500
326-
useEffect(() => {
327-
if (!error) {
328-
return;
329-
}
330-
331-
setErrorDetails({
332-
detail: 'Er is iets misgegaan bij het verzenden.',
333-
source: [],
334-
mapped: {},
335-
});
336-
}, [error]);
337-
338-
useEffect(() => {
339-
const getLocale = () => {
340-
switch (locale) {
341-
case Locale.enGB:
342-
return z.locales.en();
343-
case Locale.nlNL:
344-
return z.locales.nl();
345-
default:
346-
return z.locales.en();
347-
}
348-
};
349-
350-
z.config(getLocale());
351-
}, [locale]);
352-
353298
return (
354299
<PydanticFormContext.Provider value={PydanticFormContextState}>
355300
{children(PydanticFormContextState)}

0 commit comments

Comments
 (0)