@@ -77,22 +77,17 @@ function PydanticFormContextProvider({
7777 const formRef = useRef < string > ( formKey ) ;
7878
7979 const updateHistory = useCallback (
80- async ( formInput : object , previousSteps : object [ ] ) => {
81- const hashOfPreviousSteps = await getHashForArray ( previousSteps ) ;
82- setFormInputHistory ( ( prevState ) =>
83- prevState . set ( hashOfPreviousSteps , formInput ) ,
80+ async ( previousStepsData : object [ ] , formInputData : object ) => {
81+ const hashOfPreviousSteps = await getHashForArray (
82+ previousStepsData ,
83+ ) ;
84+ setFormInputHistory ( ( currentState ) =>
85+ currentState . set ( hashOfPreviousSteps , formInputData ) ,
8486 ) ;
8587 } ,
8688 [ ] ,
8789 ) ;
8890
89- const goToPreviousStep = ( formInput : object ) => {
90- setFormInputData ( ( prevState ) => {
91- updateHistory ( formInput , prevState ) ;
92- return prevState . slice ( 0 , - 1 ) ;
93- } ) ;
94- } ;
95-
9691 const [ errorDetails , setErrorDetails ] =
9792 useState < PydanticFormValidationErrorDetails > ( ) ;
9893
@@ -168,36 +163,38 @@ function PydanticFormContextProvider({
168163 before proceeding. If it finds data in formHistory based on the hash of the previo
169164 us steps, it uses that data to prefill the form.
170165 */
171- const awaitReset = useCallback ( async ( payLoad ?: FieldValues ) => {
172- await getHashForArray ( formInputData )
173- . then ( ( hash ) => {
174- let resetPayload = { } ;
175-
176- if ( payLoad ) {
177- resetPayload = { ...payLoad } ;
178- }
179-
180- reactHookForm . reset ( resetPayload ) ;
181- } )
182- . catch ( ( ) => {
183- console . error ( 'Failed to hash form input data' ) ;
184- } ) ;
185- } , [ ] ) ;
166+ const awaitReset = useCallback (
167+ async ( payLoad : FieldValues = { } ) => {
168+ try {
169+ reactHookForm . reset ( payLoad ) ;
170+
171+ // This is a workaround to we wait for the reset to complete
172+ // https://gemini.google.com/app/26d8662d603d6322?hl=nl
173+ return new Promise < void > ( ( resolve ) => {
174+ setTimeout ( ( ) => {
175+ resolve ( ) ;
176+ } , 0 ) ;
177+ } ) ;
178+ } catch ( error ) {
179+ console . error ( 'Failed to reactHookFOrm' , error ) ;
180+ }
181+ } ,
182+ [ reactHookForm ] ,
183+ ) ;
186184
187185 const addFormInputData = useCallback ( ( ) => {
188- setFormInputData ( ( currentInputs ) => {
186+ setFormInputData ( ( currentFormInputData ) => {
189187 // Note. If we don't use cloneDeep here we are adding a reference to the reactHookFormValues
190188 // that changes on every change in the form and triggering effects before we want to.
191189 const reactHookFormValues = _ . cloneDeep ( reactHookForm . getValues ( ) ) ;
192- updateHistory ( reactHookFormValues , currentInputs ) ;
193- return [ ...currentInputs , { ...reactHookFormValues } ] ;
190+ updateHistory ( currentFormInputData , reactHookFormValues ) ;
191+ // We call reset right after using the values to make sure
192+ // values in reactHookForm are cleared. This avoids some
193+ // race condition errors where reactHookForm data was still set where
194+ // we did not expect it to be.
195+ awaitReset ( ) ;
196+ return [ ...currentFormInputData , { ...reactHookFormValues } ] ;
194197 } ) ;
195- // setInErrorState(false);
196- // We call reset right after using the values to make sure
197- // values in reactHookForm are cleared. This avoids some
198- // race condition errors where reacfHookForm data was still set where we did not
199- /// expect it to be.
200- awaitReset ( ) ;
201198 } , [ ] ) ;
202199
203200 const submitFormFn = useCallback ( ( ) => {
@@ -217,6 +214,16 @@ function PydanticFormContextProvider({
217214 [ reactHookForm , submitFormFn ] ,
218215 ) ;
219216
217+ const goToPreviousStep = ( ) => {
218+ setFormInputData ( ( currentFormInputData ) => {
219+ // Stores any data that is entered but not submitted yet to be
220+ // able to restore later
221+ const reactHookFormValues = _ . cloneDeep ( reactHookForm . getValues ( ) ) ;
222+ updateHistory ( currentFormInputData , reactHookFormValues ) ;
223+ return currentFormInputData . slice ( 0 , - 1 ) ;
224+ } ) ;
225+ } ;
226+
220227 const submitForm = reactHookForm . handleSubmit (
221228 submitFormFn ,
222229 onClientSideError ,
@@ -287,7 +294,7 @@ function PydanticFormContextProvider({
287294 isLoading,
288295 isSending : isSending && isLoadingSchema ,
289296 onCancel,
290- onPrevious : ( ) => goToPreviousStep ( reactHookForm ?. getValues ( ) ) ,
297+ onPrevious : ( ) => goToPreviousStep ( ) ,
291298 pydanticFormSchema,
292299 reactHookForm,
293300 resetForm,
@@ -297,19 +304,34 @@ function PydanticFormContextProvider({
297304
298305 // useEffect to handle API responses
299306 useEffect ( ( ) => {
307+ const restoreHistory = async ( ) => {
308+ await getHashForArray ( formInputData )
309+ . then ( ( hash ) => {
310+ if ( formInputHistory . has ( hash ) ) {
311+ awaitReset ( formInputHistory . get ( hash ) as FieldValues ) ;
312+ } else {
313+ awaitReset ( ) ;
314+ }
315+ } )
316+ . catch ( ( ) => {
317+ console . error ( 'Failed to hash form input data' ) ;
318+ } ) ;
319+ } ;
320+
300321 if ( ! apiResponse ) {
301322 return ;
302323 }
303- // when we receive errors, we append to the scheme
324+
304325 if ( apiResponse ?. validation_errors ) {
305- // Restore the data we got the error with
306- const errorPayload = [ ... formInputData ] . pop ( ) ;
326+ // Restore the data we got the error with and remove it from
327+ // formInputData so we can add it again
307328 setFormInputData ( ( currentData ) => {
308- const newData = [ ...currentData ] ;
309- newData . pop ( ) ;
310- return newData ;
329+ const nextData = [ ...currentData ] ;
330+ const errorPayload = nextData . pop ( ) ;
331+ awaitReset ( errorPayload ) ;
332+ return nextData ;
311333 } ) ;
312- awaitReset ( errorPayload ) ;
334+
313335 setErrorDetails ( getErrorDetailsFromResponse ( apiResponse ) ) ;
314336 return ;
315337 }
@@ -319,22 +341,20 @@ function PydanticFormContextProvider({
319341 return ;
320342 }
321343
322- // when we receive a new form from JSON, we fully reset the form
323344 if ( apiResponse ?. form && rawSchema !== apiResponse . form ) {
324- awaitReset ( ) ;
325345 setRawSchema ( apiResponse . form ) ;
346+ setErrorDetails ( undefined ) ;
326347 if ( apiResponse . meta ) {
327348 setHasNext ( ! ! apiResponse . meta . hasNext ) ;
328349 }
329-
330- setErrorDetails ( undefined ) ;
350+ restoreHistory ( ) ;
331351 }
332352
333353 setIsSending ( false ) ;
334354 // eslint-disable-next-line react-hooks/exhaustive-deps
335355 } , [ apiResponse ] ) ; // Avoid completing the dependencies array here to avoid unwanted resetFormData calls
336356
337- // Useeffect to the form input data if the formKey changes
357+ // useEffect to the form input data if the formKey changes
338358 useEffect ( ( ) => {
339359 if ( formKey !== formRef . current ) {
340360 setFormInputData ( [ ] ) ;
@@ -343,22 +363,22 @@ function PydanticFormContextProvider({
343363 }
344364 } , [ formKey ] ) ;
345365
346- // UseEffect to handle successfull submits
366+ // useEffect to handle successfull submits
347367 useEffect ( ( ) => {
348368 if ( ! isFullFilled ) {
349369 return ;
350370 }
351371
352372 if ( onSuccess ) {
353- const values = reactHookForm . getValues ( ) ;
354- onSuccess ( values , apiResponse || { } ) ;
373+ const reactHookFormValues = _ . cloneDeep ( reactHookForm . getValues ( ) ) ;
374+ onSuccess ( reactHookFormValues , apiResponse || { } ) ;
355375 }
356376
357377 setFormInputHistory ( new Map < string , object > ( ) ) ;
358378 // eslint-disable-next-line react-hooks/exhaustive-deps
359379 } , [ isFullFilled ] ) ; // Avoid completing the dependencies array here to avoid unwanted resetFormData calls
360380
361- // UseEffect to handles errors throws by the useApiProvider call
381+ // useEffect to handles errors throws by the useApiProvider call
362382 // for instance unexpected 500 errors
363383 useEffect ( ( ) => {
364384 if ( ! error ) {
@@ -372,7 +392,7 @@ function PydanticFormContextProvider({
372392 } ) ;
373393 } , [ error ] ) ;
374394
375- // UseEffect to handle locale change
395+ // useEffect to handle locale change
376396 useEffect ( ( ) => {
377397 const getLocale = ( ) => {
378398 switch ( locale ) {
0 commit comments