skip to Main Content

I have a multi-step form (react-hook-form) in Next.js. The onSubmit() event lives in a component called from the layout, and I’d like to be able to add a spinner and disable the submit button. How can I know on the page when onSubmit has been called?

layout.tsx –

export default function Layout({ children }: { children: React.ReactNode }) {
  return (
    <div>
      <Sidebar />
      <Provider>{children}</Provider>
    </div>
  );
}

Provider.tsx

export default function Provider({ children }: FormProviderProps) {

const onMyFormSubmit = async (data: FormTypes) => {
    await createStory(data);
  }

return (
    <FormProvider {...methods}>
      {/* <form onSubmit={methods.handleSubmit(onSubmit)} className="flex w-full"> */}
      <form onSubmit={methods.handleSubmit((data) => onMyFormSubmit(data))} className="flex w-full">
        {children}
      </form>
    </FormProvider>
  );
}

interface FormProviderProps {
  children: React.ReactNode;
}

page.tsx –

export default function SummaryPage() {
return (
    <div>
    <FormWrapper heading="Finishing up" >
      <div className="flex flex-col bg-alabaster rounded-lg px-4 lg:px-6 mt-6 w-full shrink-0">
        <div className="flex items-center justify-between pt-4 pb-3 lg:pb-5">
        <button
          type="submit"
          disabled={prop I need from onSubmit event}
          className=".."
        >
          Submit story
        </button>
        <div className={prop I need from onSubmit event?  undefined : 'hidden'}>
          <Spinner height="80" width="80" color="#713F12" />
        </div>
        </div>
      </div>
    </div>
  );  
}

I’m new to Next and react, so would really appreciate guidance here.
I’ve looked at react context, but as far as I understand it, it doesn’t seem dynamic (once an object has passed, it doesn’t update)

2

Answers


  1. Chosen as BEST ANSWER

    I ended up using the isSubmitting state and useEffect to capture when it changes.

    export default function SummaryPage() {
    const { watch, formState: { isSubmitting }} = useAppFormContext();
    const [hasSubmitted, setHasSubmitted] = useState(false);
    
    React.useEffect(() => {
        if (isSubmitting) { setHasSubmitted(true) }
      }, [isSubmitting]); 
      
      return (
        <div>
        <FormWrapper>
        <button
              type="submit"
              disabled={hasSubmitted}
              >
        </FormWrapper>
        </div>
      );  
    }

    I use useEffect instead of isSubmitting directly since it quickly reverts back to false once submission has been completed


  2. You can pass a callback function as props and use it to notify a parent component

    export default function SummaryPage({ disableSubmit, onSubmitCallback }) {
    return (
        <div>
        <FormWrapper heading="Finishing up" >
          <div className="flex flex-col bg-alabaster rounded-lg px-4 lg:px-6 mt-6 w-full shrink-0">
            <div className="flex items-center justify-between pt-4 pb-3 lg:pb-5">
            <button
              type="submit"
              onClick={onSubmitCallback} 
              disabled={disableSubmit}
              className=".."
            >
              Submit story
            </button>
            <div className={prop I need from onSubmit event?  undefined : 'hidden'}>
              <Spinner height="80" width="80" color="#713F12" />
            </div>
            </div>
          </div>
        </div>
      );  
    }
    

    then you’d toggle the disableSubmit in your parent component before and after recieving the reponse which will send the buttons ‘disableSubmit’ state back down

    disclaimer:

    this is a gist that should come close hopefully answer your question. (you may chose to apply the suggestion to the onsubmit of the form over attaching it directly to the button as done above)

    But would recommend simplifying it by having the form component itself handle the async request.

    if multiple components need to know then implementing a global state provider (same idea as the form provider just wrap everything from index.js/ts), that can toggle a "loading" state so any component that needs to know about can react to it directly.

    instead of bubbling callbacks up and down through each component as it gets messy fast. just a imo; if I had to send a callback up through multiple components might reconsider the component architecture.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search