@@ -34,6 +34,7 @@ import {
3434import {
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