77 */
88import { cloneElement , useEffect , type ReactElement , type ReactNode } from 'react'
99import type { FieldValues , UseFormReturn } from 'react-hook-form'
10- import { useBlocker , type Blocker } from 'react-router-dom'
10+ import { useBlocker , type unstable_Blocker as Blocker } from 'react-router-dom'
1111
1212import type { ApiError } from '@oxide/api'
1313import { Modal , PageHeader , PageTitle } from '@oxide/ui'
@@ -25,11 +25,7 @@ interface FullPageFormProps<TFieldValues extends FieldValues> {
2525 error ?: Error
2626 form : UseFormReturn < TFieldValues >
2727 loading ?: boolean
28- /**
29- * Use await mutateAsync(), otherwise you'll break the logic below that relies
30- * on knowing when the submit is done.
31- */
32- onSubmit : ( values : TFieldValues ) => Promise < void >
28+ onSubmit : ( values : TFieldValues ) => void
3329 /** Error from the API call */
3430 submitError : ApiError | null
3531 /**
@@ -57,25 +53,22 @@ export function FullPageForm<TFieldValues extends FieldValues>({
5753 onSubmit,
5854 submitError,
5955} : FullPageFormProps < TFieldValues > ) {
60- const { isSubmitting, isDirty, isSubmitSuccessful } = form . formState
56+ const { isSubmitting, isDirty } = form . formState
6157
62- // Confirms with the user if they want to navigate away if the form is
63- // dirty. Does not intercept everything e.g. refreshes or closing the tab
64- // but serves to reduce the possibility of a user accidentally losing their
65- // progress.
66- const blocker = useBlocker ( isDirty && ! isSubmitSuccessful )
58+ /*
59+ Confirms with the user if they want to navigate away
60+ if the form is dirty. Does not intercept everything e.g.
61+ refreshes or closing the tab but serves to reduce
62+ the possibility of a user accidentally losing their progress
63+ */
64+ const blocker = useBlocker ( isDirty )
6765
68- // Gating on !isSubmitSuccessful above makes the blocker stop blocking nav
69- // after a successful submit. However, this can take a little time (there is a
70- // render in between when isSubmitSuccessful is true but the blocker is still
71- // ready to block), so we also have this useEffect that lets blocked requests
72- // through if submit is succesful but the blocker hasn't gotten a chance to
73- // stop blocking yet.
66+ // Reset blocker if form is no longer dirty
7467 useEffect ( ( ) => {
75- if ( blocker . state === 'blocked' && isSubmitSuccessful ) {
76- blocker . proceed ( )
68+ if ( blocker . state === 'blocked' && ! isDirty ) {
69+ blocker . reset ( )
7770 }
78- } , [ blocker , isSubmitSuccessful ] )
71+ } , [ blocker , isDirty ] )
7972
8073 const childArray = flattenChildren ( children )
8174 const actions = pluckFirstOfType ( childArray , Form . Actions )
@@ -88,27 +81,24 @@ export function FullPageForm<TFieldValues extends FieldValues>({
8881 < form
8982 className = "ox-form pb-20"
9083 id = { id }
91- onSubmit = { async ( e ) => {
84+ onSubmit = { ( e ) => {
9285 // This modal being in a portal doesn't prevent the submit event
9386 // from bubbling up out of the portal. Normally that's not a
9487 // problem, but sometimes (e.g., instance create) we render the
9588 // SideModalForm from inside another form, in which case submitting
9689 // the inner form submits the outer form unless we stop propagation
9790 e . stopPropagation ( )
98- // Important to await here so isSubmitSuccessful doesn't become true
99- // until the submit is actually successful. Note you must use await
100- // mutateAsync() inside onSubmit in order to make this wait
101- await form . handleSubmit ( onSubmit ) ( e )
91+ // This resets `isDirty` whilst keeping the values meaning
92+ // we are not prevented from navigating away by the blocker
93+ form . reset ( { } as TFieldValues , { keepValues : true } )
94+ form . handleSubmit ( onSubmit ) ( e )
10295 } }
10396 autoComplete = "off"
10497 >
10598 { childArray }
10699 </ form >
107100
108- { /* rendering of the modal must be gated on isSubmitSuccessful because
109- there is a brief moment where isSubmitSuccessful is true but the proceed()
110- hasn't fired yet, which means we get a brief flash of this modal */ }
111- { ! isSubmitSuccessful && < ConfirmNavigation blocker = { blocker } /> }
101+ { blocker ? < ConfirmNavigation blocker = { blocker } /> : null }
112102
113103 { actions && (
114104 < PageActions >
0 commit comments