Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[NEXT-1168] useFormStatus doesn't seem to work with server actions #49232

Closed
1 task done
rliang opened this issue May 4, 2023 · 52 comments
Closed
1 task done

[NEXT-1168] useFormStatus doesn't seem to work with server actions #49232

rliang opened this issue May 4, 2023 · 52 comments
Labels
area: app App directory (appDir: true) bug Issue was opened via the bug report template. linear: next Confirmed issue that is tracked by the Next.js team.

Comments

@rliang
Copy link

rliang commented May 4, 2023

Verify canary release

  • I verified that the issue exists in the latest Next.js canary release

Provide environment information

Binaries:
      Node: 20.0.0
      npm: 9.6.4
      Yarn: 1.22.17
      pnpm: 7.27.0
    Relevant packages:
      next: 13.4.0
      eslint-config-next: N/A
      react: 0.0.0-experimental-aef7ce554-20230503
      react-dom: 0.0.0-experimental-aef7ce554-20230503

Which area(s) of Next.js are affected? (leave empty if unsure)

App directory (appDir: true)

Link to the code that reproduces this issue

https://github.com/rliang/next-use-form-status-bug-reproduction

To Reproduce

  • run npm run dev
  • open localhost:3000 in the browser
  • click on the submit button

Describe the Bug

useFormStatus doesn't work, such that its return value is never updated when submitting a form.

Expected Behavior

The page should update the formStatus after clicking on submit.

Which browser are you using? (if relevant)

No response

How are you deploying your application? (if relevant)

No response

NEXT-1168

@rliang rliang added the bug Issue was opened via the bug report template. label May 4, 2023
@github-actions github-actions bot added the area: app App directory (appDir: true) label May 4, 2023
@rliang
Copy link
Author

rliang commented May 4, 2023

Moreover, the terminal also logs an error:

error - TypeError: (0 , react_dom__WEBPACK_IMPORTED_MODULE_1__.experimental_useFormStatus) is not a function

Even though the initial state is correctly displayed in the browser.

@john-griffin
Copy link

Just hit this too

@mauricekleine
Copy link

I was able to get rid of the error by installing the next/canary builds of React and ReactDOM, however I still don't see updated values from useFormStatus after I submit my form

@rliang
Copy link
Author

rliang commented May 5, 2023

Found out that useFormStatus in fact looks up the status of the nearest <form> parent element, i.e. the element that uses this hook must be a child of a <form>.

Still getting the error in the server console though, even after upgrading next/react/react-dom.

@megadrive
Copy link

I have an incredibly simple component, hitting the Error: (0 , react_dom__WEBPACK_IMPORTED_MODULE_3__.experimental_useFormStatus) is not a function issue.

Installed react@next and react-dom@next and no change unfortunately.

@ElvarP
Copy link

ElvarP commented May 10, 2023

Also having the same issue.

@felipebarcelospro
Copy link

Same error here!

@harrybawsac
Copy link

harrybawsac commented May 10, 2023

Edit

I managed to get rid of the experimental_useFormStatus is not a function error with the following versions:

{
    "react": "experimental",
    "react-dom": "experimental",
    "next": "canary"
}

But it's only for client components. So I get that error on server components.

You're importing a component that needs experimental_useFormStatus. It only works in a Client Component but none of its parents are marked with "use client", so they're Server Components by default.


Original

@mauricekleine

How did you manage to get rid of experimental_useFormStatus is not a function exactly? The following way?

{
    "react": "next",
    "react-dom": "next",
    "next": "canary"
}

@Gregoor
Copy link

Gregoor commented May 16, 2023

Found out that useFormStatus in fact looks up the status of the nearest <form> parent element, i.e. the element that uses this hook must be a child of a <form>.

Still getting the error in the server console though, even after upgrading next/react/react-dom.

Thanks, I got it to work with this comment without having to resort to full-experimental mode, i.e. this works:

// app/.../page.tsx
"use server";
import { ClientComponent } from "./ClientComponent";

async function doSomething() {}

export default function Page() {
  return <form action={doSomething}><ClientComponent /></form>;
}
// app/.../ClientComponent.tsx
"use client";
import { experimental_useFormStatus as useFormStatus } from "react-dom";

export function ClientComponent() {
  const formStatus = useFormStatus();
  return <input type="submit" disabled={formStatus.pending}>Go</input>;
}

@harrybawsac
Copy link

@Gregoor Yeah that works because you're using it in a client component. Documentation doesn't state that:

image

@brunoalano
Copy link

@Gregoor You found a way to retrieve the response from the server action? I want to receive if had an error on server-side, and display that on my client component.

@thai-recurmetrics
Copy link

any idea why the pending flag does not update when the form is submitting? It’s always false.

@Gregoor
Copy link

Gregoor commented May 16, 2023

@Gregoor Yeah that works because you're using it in a client component. [Documentation doesn't state that]

Yes-ish, but it did not work when <form> was within ClientComponent (formStatus would not update). I'm sensing there is sth like React's Context going on here.

@Gregoor You found a way to retrieve the response from the server action? I want to receive if had an error on server-side, and display that on my client component.

Not yet, but I'd like to know that too

@timneutkens timneutkens added the linear: next Confirmed issue that is tracked by the Next.js team. label May 17, 2023
@timneutkens timneutkens changed the title useFormStatus doesn't seem to work with server actions [NEXT-1168] useFormStatus doesn't seem to work with server actions May 17, 2023
@sebastianpikand
Copy link

So... what is the recommended way of showing a loading status for server-component form submissions?

@arjunkomath
Copy link

So... what is the recommended way of showing a loading status for server-component form submissions?

The only solution that has worked for me is having the form and server action in a server component, then have a client component inside the form (should be a child of the form) consuming useFormStatus.

You can find a full working example here: https://github.com/techulus/manage-prompt/blob/c9dad49a802c2192d1553d62e4b3151dda134fdf/app/console/workflows/new/page.tsx#L7

@dvrd
Copy link

dvrd commented May 26, 2023

Is there a way for the form to avoid rerender & the useFormStatus hook to work? It seems to be connected to the request sent by the form but it rerender the entire form once the request is returned & if you avoid the post by handling the server action and passing false to the on submit the useFormStatus hook does not changes state

@harrybawsac
Copy link

Documentation has been updated with NextJS v13.4.5.

@JLarky
Copy link

JLarky commented Jun 20, 2023

Just in case if anyone is getting caught on the part where return value is not updated for a submitted form, I wrote a small utility that could help you with that. It allows you to lift the state of useFormStatus from child component up to the parent.

https://gist.github.com/JLarky/190bab52ff13c44f9420523d1792fbf0

@stevewillard
Copy link

Just in case if anyone is getting caught on the part where return value is not updated for a submitted form, I wrote a small utility that could help you with that. It allows you to lift the state of useFormStatus from child component up to the parent.

https://gist.github.com/JLarky/190bab52ff13c44f9420523d1792fbf0

Thank you! I appreciate the gist -- it worked great.

@malith7597
Copy link

malith7597 commented Jul 15, 2023

In next.config.js enable server actions.Don't forget to use npm run dev to build the project again. This worked for me.

const nextConfig = { experimental:{ serverActions:true, }, }

@sojinsamuel
Copy link

In next.config.js enable server actions.Don't forget to use npm run dev to build the project again. This worked for me.

const nextConfig = { experimental:{ serverActions:true, }, }

This worked for me too

@MincePie
Copy link

Did anyone figure this out? I'm struggling to get this repo working with this error: https://github.com/steven-tey/prisma-server-actions

@ahmadawais
Copy link
Contributor

I have tried a lot of different ways, but the API is definitely experimental and breaks. So, reverted back to not using form action for complex form processing.

@maornissan
Copy link

I got a TS error when trying to import:

import { experimental_useFormStatus as useFormStatus } from "react-dom";

When ignoring this error, problem seems to be solved

// ts-ignore because experimental_useFormStatus is not in the types
// @ts-ignore
import { experimental_useFormStatus as useFormStatus } from "react-dom";

@MiguelGuerreroGoodRx
Copy link

Does anyone knows how to make this work with jest+testing library and with storybook, in both integration and visual tests i am getting (0 , react_dom__WEBPACK_IMPORTED_MODULE_1__.experimental_useFormState) is not a function

@panoskouff
Copy link

Found out that useFormStatus in fact looks up the status of the nearest <form> parent element, i.e. the element that uses this hook must be a child of a <form>.

Still getting the error in the server console though, even after upgrading next/react/react-dom.

Has anyone found a way to get around that ?

what if I want to have

export default function Contact() {
  const [state, formAction] = useFormState(myAction, initialState);
  const { pending } = useFormStatus();
  
  return <div>
    {pending ? <h1>Loading...<h1> : ''}
    <div>
      ...
        <form action={formAction}>
        ....

@kuijpie
Copy link

kuijpie commented Oct 14, 2023

Has anyone found a way to get around that ?

what if I want to have

export default function Contact() {
  const [state, formAction] = useFormState(myAction, initialState);
  const { pending } = useFormStatus();
  
  return <div>
    {pending ? <h1>Loading...<h1> : ''}
    <div>
      ...
        <form action={formAction}>
        ....

@panoskouff useFormStatus can only be used inside a client component, which is a child of a <form>.
So what you can do is create a separate loading client component and place that inside your form.

"use client"

export default function SubmitButton() {
  const { pending } = useFormStatus()

  return (
    <button type="submit" disabled={pending}>{pending ? 'Loading...' : 'Submit'}</button> 
  )
}
export default function Contact() {
  return (
    <form action={formAction}>
       ...
       <SubmitButton />
    </form>
  )
}

If you don't want anything to be visible from the form, you could make all your inputs part of a client component.

"use client"

export default FormInputs() {
  const { pending } = useFormStatus()

  return (
    <>
      {pending ? (
        <h1>Loading...<h1>
      ) : (
         <>
           <label><input /></label>
           ...
         </>
      )}
    </>
  )
}

@micheltucker
Copy link

micheltucker commented Oct 18, 2023

Adding my learnings going through this. The documentation of NextJS is really not complete. Best would be to watch the video on Youtube: https://www.youtube.com/watch?v=dDpZfOQBMaU and check its repo here: https://github.com/vercel/next.js/tree/canary/examples/next-forms

Maybe I am missing something, but the docs say import { useFormStatus } from 'react-dom' whilst the repo includes it experimentally import { experimental_useFormStatus as useFormStatus } from 'react-dom'. Plus, y'all need to either update typescript definitions or // @ts-expect-error.

Am I missing something here?

Update:
just saw one of the comments suggested the following, which would be the alternative (more global) approach. I still believe the docs should be updated to give a better overview and examples, just as the YouTube video does.

{
    "react": "experimental",
    "react-dom": "experimental",
    "next": "canary"
}

@hyper-dot
Copy link

I got a TS error when trying to import:

import { experimental_useFormStatus as useFormStatus } from "react-dom";

When ignoring this error, problem seems to be solved

// ts-ignore because experimental_useFormStatus is not in the types
// @ts-ignore
import { experimental_useFormStatus as useFormStatus } from "react-dom";

But while deploying in vercel I will throw same error. Locally it runs despite of typescript error.

@WilsonKinyua
Copy link

WilsonKinyua commented Oct 23, 2023

I got a TS error when trying to import:

import { experimental_useFormStatus as useFormStatus } from "react-dom";

When ignoring this error, problem seems to be solved

// ts-ignore because experimental_useFormStatus is not in the types
// @ts-ignore
import { experimental_useFormStatus as useFormStatus } from "react-dom";

But while deploying in vercel I will throw same error. Locally it runs despite of typescript error.

Above to work locally and on vercel, have it like this:

// @ts-expect-error
import { experimental_useFormStatus as useFormStatus } from "react-dom";

@devmetal
Copy link

Hi, the documentation needs to be updated in nextjs. They even did not mention this experimental stuff. Also i'm kinda start get frustrated these days. I'm currently in a migration to nextjs with my pet project and keep hit these kind of walls. Started with prisma and edge runtime. I know this is not easy and i'm really appriciate the work behind this meta framework but this server side action things needs to be clearified. Most of these kind of frameworks already can provide a single loading state from server side action. I think the workaround maybe the startTransition hook but i don't know because this is very under documented. Sorry for this but i'm overall kinda frustrated the current frontend era....

@AccelKing12
Copy link

I see that this nextjs product have import { experimental_useFormStatus as useFormStatus } from "react-dom";
in the addButton.tsx with no error and no // @ts-expect-error
https://github.com/saleor/storefront

@WallisMark
Copy link

how was this problem solved im still very much confused

@xmayukx
Copy link

xmayukx commented Oct 26, 2023

I updated NextJS 13.5.5 to 13.5.7, it worked for me.
Just run npm i next@canary

how was this problem solved im still very much confused

@nebrekab
Copy link

This does work in NextJS 13.5.6, but now seems to be broken in NextJS 14.0.0.

Anyone else having this issue?

@micheltucker
Copy link

micheltucker commented Oct 27, 2023

This does work in NextJS 13.5.6, but now seems to be broken in NextJS 14.0.0.

Can you be more specific? What I shared above (#49232 (comment)) works for me.

@nebrekab
Copy link

Ignore me... Apologies....

I was missing my morning coffee and not reading error responses... and on my second attempt see it's working.

Our code for NextJS 13.5.6 works in NextJS 14.0.0, with the only change being to remove the 'experimental_' prefix within the imports (both for useFormState and useFormStatus).

@ajiohjesse
Copy link

I just updated to next.js 14. I had to add @ts-expect-error

// @ts-expect-error
import { useFormStatus } from 'react-dom';

else typescript throws an error that useFormStatus is not exported . It works fine for me now though.

@ilia-luk
Copy link

In my case the TS module resolution error was solved by adding yarn add -D @types/react-dom instead of using // @ts-expect-error

@omburj
Copy link

omburj commented Nov 3, 2023

refer to the NextJS latest example https://github.com/vercel/next.js/tree/canary/examples/next-forms
in that use correct the verion of react-dom in package.json to latest. This will resolve the pnpm run build error
"@types/react-dom": "^18.2.14",

@micheltucker
Copy link

refer to the NextJS latest example https://github.com/vercel/next.js/tree/canary/examples/next-forms in that use correct the verion of react-dom in package.json to latest. This will resolve the pnpm run build error "@types/react-dom": "^18.2.14",

Yes! See above: #49232 (comment)

@omburj
Copy link

omburj commented Nov 3, 2023 via email

@mtt786
Copy link

mtt786 commented Nov 15, 2023

Started working for me after running yarn add next@latest 👻.

@Nlferu
Copy link

Nlferu commented Nov 16, 2023

I still cannot get rid of this error TypeError: (0 , react_dom__WEBPACK_IMPORTED_MODULE_2__.useFormStatus) is not a function. I have tried all solutions mentioned above, but none had effect, experimental or not did not matter. I'm using this function in my other project and it is working perfectly fine. I have copied all dependencies versions from there but still no effect on my current project...

Any other ideas how to get rid of this error?

@Steravy
Copy link

Steravy commented Nov 18, 2023

Hi in my case i was using the useFormStatus in the same component as the form to disable the submit button when the for is submiting. and i got that issue: TypeError: (0 , react_dom__WEBPACK_IMPORTED_MODULE_2__.useFormStatus) is not a function.

I created a separate component, lets called SubmitButton and i placed the useFormStatus and call it there, then i import the component and used as a child of the form, and then i used the ts annotation: // @ts-ignore import { experimental_useFormStatus as useFormStatus } from "react-dom"; mentioned by @WilsonKinyua

The button component:

`
const SubmitButton = () => {

const {pending} = useFormStatus();

return (
    <Button type="submit"
            className="group rounded-full h-[3rem] w-[8rem] flex gap-2 outline-none transition-all focus:scale-110 hover:scale-110 hover:bg-gray-950 active:scale-105 dark:bg-white dark:bg-opacity-10 disabled:scale-100 disabled:bg-opacity-65">
        {
            pending
                ?
                (
                    <div className="h-5 w-5 rounded-full border-b-2 border-white animate-spin"/>
                )
                :
                (
                    <>
                        Submit{" "}
                        <FaPaperPlane
                            className="text-xs opacity-70 transition-all group-hover:translate-x-1 group-hover:-translate-y-1"/>{" "}
                    </>
                )
        }

    </Button>
)

}
`

@mertthesamael
Copy link

I just fixed it by changing versions of:

"@types/react-dom": "18.2.17",
"@types/react": "18.2.39",
"next": "canary",
"eslint-config-next": "canary",
"react": "next",
"react-dom": "next",

with the given values. I hope that works.

@dospolov
Copy link

dospolov commented Dec 2, 2023

@kuijpie thank you 🙏🏻

the only thing that helped me is to place action and form tag in a Server component, but the form content in JSX.fragment inside Client component.

@hsglc
Copy link

hsglc commented Dec 9, 2023

If you're using the next and react libraries with their experimental version, you should upgrade your @types/react-dom to 18.2.7 version, and error goes to the void. I mean it worked for me!

@LassazVegaz
Copy link

I got the error TypeError: (0 , react_dom__WEBPACK_IMPORTED_MODULE_1__.useFormStatus) is not a function

For me useFormStatus is shown as stable.

"use client";
import { Button, ButtonProps } from "@mui/material";
import { useFormStatus } from "react-dom";

const FormButton = (props: ButtonProps) => {
  const { pending } = useFormStatus();

  return <Button disabled={pending} {...props} />;
};

export default FormButton;

The above code does not give a TS error but gives the runtime error mentioned earlier. The reason is types and the implementations do not match. In implementation, it is still experimental. So I had to do this,

"use client";
import { Button, ButtonProps } from "@mui/material";
// import { useFormStatus } from "react-dom";
// @ts-expect-error Types and implementations do not match
import { experimental_useFormStatus as useFormStatus } from "react-dom";

const FormButton = (props: ButtonProps) => {
  const { pending } = useFormStatus();

  return <Button disabled={pending} {...props} />;
};

export default FormButton;

Now the runtime error goes away and useFormStatus works (functionally) as expected.

in package.json versions look like this,

  "dependencies": {
    "next": "13.5.4",
    "react": "^18",
    "react-dom": "^18"
  },
  "devDependencies": {
    "@types/react": "^18",
    "@types/react-dom": "^18"
  }

I have no idea how to do version matching here because they all have the same major release version.

@LassazVegaz
Copy link

After upgrading to Next 14 (14.0.4 to be exact), I could import useFormStatus (the stable one).

@johnsonfash
Copy link

The only way useFormStatus work is As a hook, it can only live in a client component and can only be used as a child of a form element using a Server Action.

So convert the button, input to a seperate client component and place the component inside a form

ie.
submitHandle.tsx

''use server'
export const submit = (e: FormData)=> {
const name = e.get('name')
}

customButton.tsx
export const CustomButton = ()=> {
const {pending} = useFormStatus();
return submit
}

login.tsx
"use client"

@leerob
Copy link
Member

leerob commented Dec 30, 2023

We now have an example of forms + useFormStatus + useFormState using TypeScript and all of the latest versions of the packages. This is linked in the docs and we've made quite a few iterations there since this issue was opened. Since the originally reproduction is solved, going to close this out – if there are any new issues, let's start a new thread with a new reproduction. Thank you!

@leerob leerob closed this as completed Dec 30, 2023
@vercel vercel locked as resolved and limited conversation to collaborators Dec 30, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area: app App directory (appDir: true) bug Issue was opened via the bug report template. linear: next Confirmed issue that is tracked by the Next.js team.
Projects
None yet
Development

No branches or pull requests