Nextjs 13 server actions and useState
Asked Answered
S

3

6

I'm using Nextjs 13 with server actions. I have a form:

const [isUploadingDocs, setIsUploadingDocs] = useState<boolean>(false);
... rest of code ...
return (
<form
  action={async (formData) => {
  setIsUploadingDocs(true)
  upload(formData).then((response) => {
  setIsUploadingDocs(false)
  })
}}>
)

I am never able to capture the state change within the above action. I've also tried useRef, and while that worked within the function, it did not trigger react's digest cycle, so my elements depending on the ref.current value were unchanged.

I read the documentation here and also tried useFormStatus, which also did not work as expected in my implementation. I'd rather just use useState if possible.

Is this a bug with nextjs 13 or expected?

Shaddock answered 18/8, 2023 at 3:13 Comment(3)
Can you assign boolean value to your string type state?Megacycle
I edited -- this was a bad copy and paste. the useState value is true/false, this is not the reason for the failure.Shaddock
Did you make sure the useFormStatus was a child of the form?Sogdian
P
4

The problem is Nextjs context separation.

Server actions work inside server context, and 'client-only' features don't work inside server context. React hooks like useState, useEffect, useMemo, useRouter, etc. run inside client context only. I think these docs may help you a bit.

I also wrote a broad explanation of "client or server" separating behaviour. Maybe that answer will help.

Punishment answered 15/9, 2023 at 16:24 Comment(3)
would it be the same answer if say you were trying to pass state data into a server action function? I can access formdata but cant I get anything else ?Doubleganger
@Doubleganger as I remember, yes. "formdata" feature is the only actual server-side feature that you can access from your client code directly. And that feature creates a lot of confusion.Punishment
@VictorGorban while you're correct about the Server Actions running server-side, and useState running client-side, I don't think that's what's happening here. He's using useState, so it's a Client Component calling the Server Action.Sogdian
G
2

In Next.js, the purpose of Server Actions is to handling forms in server components. Back in time, since Server Components only work if there is no interactivity it was way hard to make that and therefore they made this thing. UseState, useRef, useEffect and any sort of hooks similar to these which uses client's browser won't work with server actions.

The reason why ref.current was unchanged is not bug, since the formdata runs in server, it doesn't aware of the ref so you can't read it form there. You can't use useState either if you insist to use server actions.

I would recommend you to not use server actions if you need to display something in the inputs and show some clickable effects, since server actions are generally meant for api calls which shouldn't be bother user (analytics, user information etc.); However if you really want to use it, i would recommend revalidateTag or revalidatePath, with this way if you place form actions in the respective manner, you can achieve what you want i suppose.

Ginder answered 4/9, 2023 at 18:53 Comment(0)
P
0

I've faced the same problem and I've found the solution on https://nextjs.org/docs/pages/building-your-application/data-fetching/forms-and-mutations#displaying-loading-state

I think you should use 'onSubmit' event instead of 'action' in <form onSubmit={onSubmit}...

Where the onSubmit function looks like below.

import { useState, FormEvent } from "react";
    
async function onSubmit(event: FormEvent<HTMLFormElement>) {
        event.preventDefault();
        setIsLoading(true); // Set loading to true when the request starts
        const formData = new FormData(event.currentTarget); //get formData from event
        await action(formData); //Server action
        setIsLoading(false); // Set loading to false when the request completes
      }
Paddlefish answered 16/2 at 15:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.