facebook/react

[React 19] Incorrect infered response type in server actions with React 19

alexanderalmstrom opened this issue · 0 comments

Summary

Server actions in Next.js return Promise<void> instead of the infered response type.

This is most likely a issue in react 19 or @types/react 19 since it works as exepected with both Next.js 14 and Next.js 15 and can only be reproduced when upgrading from React 18 to 19.

Example action:
https://github.com/alexanderalmstrom/next-15-server-actions/blob/main/src/app/actions.ts

main branch with React 19:
https://github.com/alexanderalmstrom/next-15-server-actions/tree/main

Skärmavbild 2024-12-14 kl  23 49 34

react-18 branch with React 18.3.1 where the response is infered correctly with Promise<{ message: string }>
https://github.com/alexanderalmstrom/next-15-server-actions/tree/react-18

Skärmavbild 2024-12-14 kl  23 42 07

Both branches uses the same Next version 15.1.0 and the same server action.

I'm aware of the useActionState (previously called useFormState)...

"use client";

import { type ComponentProps, useActionState } from "react";
import { exampleFormActionState } from "../actions";

export default function FormWithActionState(props: ComponentProps<"form">) {
  const [state, action] = useActionState(exampleFormActionState, {
    message: "",
  });

  return (
    <form action={action} {...props}>
      <input type="text" name="name" />
      <button type="submit">Submit</button>
      {state.message && <p>{state.message}</p>}
    </form>
  );
}

but is it expected that the action should return Promise<void>?

Skärmavbild 2024-12-15 kl  00 20 04

and we have to type the state like this, instead of having the types infered automatically for us in typescript?

type FormState = {
  message: string;
};

export async function exampleFormActionState(
  prevState: FormState,
  formData: FormData
) {
  const name = formData.get("name");

  if (!name || typeof name !== "string") {
    return { message: "VALIDATION_ERROR" };
  }

  return { message: `Hello, ${name}!` };
}

The thing is, what if I don't want to use useActionState? Lets say that we pass the action as prop to the form element, and then use auseFormStatus and a pending state for the button. Then that's not possible because of this error.