Fer Toasted
Fer Toasted

Reputation: 1738

Pass arguments to a server action using experimental_useFormState

I'm trying out this new NextJs feature documented here

The base example works whenever action doesn't receive any argument at all.

But I'm wondering how do I pass arguments to the server action.

  // someForm.tsx

  import { experimental_useFormState as useFormState } from 'react-dom';
  import submit from '@/app/actions';

  // passing the action as the first argument
  const [_formStatus, formAction] = useFormState(submit, {
    message: '',
  });

  // jsx
        <form
          action={formAction}
        >
          <input
            aria-label='email address'
            value='foo'
            type='text'
            placeholder='Enter your email'
          />
          <Button type='submit' pendingMessage='Sending...'>Contact Us</Button>
        </form>

as mentioned, this action doesn't take any argument an it works

// app/actions

// what if I'd wanted submit to have arguments from the form?
export default async function submit() {
  console.log('submitting');
  try {
    await new Promise<void>((res) => {
      setTimeout(() => {
        res();
      }, 2000);
    });
    revalidatePath('/');
    return { message: 'Thanks!' };
  } catch (e) {
    console.error(e);
    return { error: 'Error' };
  }
}

I tried adding an argument with the type of FormData but it doesn't work

export default async function submit(formData: FormData) {

no overload matches this call. Overload 1 of 2, '(action: (state: FormData) => Promise, initialState: FormData, permalink?: string | undefined): [state: FormData, dispatch: () => void]', gave the following error. Argument of type '(formData: FormData) => Promise<{ message: string; error?: undefined; } | { error: string; message?: undefined; }>' is not assignable to parameter of type '(state: FormData) => Promise'. Type 'Promise<{ message: string; error?: undefined; } | { error: string; message?: undefined; }>' is not assignable to type 'Promise'. Type '{ message: string; error?: undefined; } | { error: string; message?: undefined; }' is not assignable to type 'FormData'. Type '{ message: string; error?: undefined; }' is missing the following properties from type 'FormData': append, delete, get, getAll, and 7 more. Overload 2 of 2, '(action: (state: FormData, payload: unknown) => Promise, initialState: FormData, permalink?: string | undefined): [state: FormData, dispatch: (payload: unknown) => void]', gave the following error. Argument of type '(formData: FormData) => Promise<{ message: string; error?: undefined; } | { error: string; message?: undefined; }>' is not assignable to parameter of type '(state: FormData, payload: unknown) => Promise'. Type 'Promise<{ message: string; error?: undefined; } | { error: string; message?: undefined; }>' is not assignable to type 'Promise'.ts(2769)

Upvotes: 7

Views: 6986

Answers (2)

Jonathan Beech
Jonathan Beech

Reputation: 1

Agreed you need to add input elements with a name.

For adding hard coded values.
I saw in a tutorial binding to the function but that doesn't play nice with typescript:

<form action={addToCart.bind(product.id)}>

I needed to add a few hard coded properties so I used a "json field" like this:

<form action={addToCart}>
    <input type="hidden" name="json" value={JSON.stringify({productId: product.id, variant: product.variants[0].id, qty: 3})} />
    <button>Add to cart</button>
</form>

Upvotes: 0

simplikios
simplikios

Reputation: 381

You can pass values via input elements. The thing you forgot is to give those inputs a name attribute.

//...
<input
  name="email"
  aria-label="email address"
  value="foo"
  type="text"
  placeholder="Enter your email"
/>

And in the action you need to pass the FormData and prevState arguments:

export default async function submit(prevState, formData) {
  // getting your input value
  const email = formData.get("email");
  // ...
}

Here's an official example for reference.

Upvotes: 3

Related Questions