Next.js Discord

Discord Forum

Can I pass an array on the server action? If so, how?

Answered
Jenn posted this in #help-forum
Open in Discord
I have this cart which is an array. In my Order Page client component, I am submitting the customer data which works well with the server action. The next thing I need is the data of the cart. However, I do not have any idea on how I can pass the array cart on my server action page.

Also, for the cart I need to save it on the table:
create table
  public.order_items (
    order_items_id uuid not null default gen_random_uuid (),
    quantity numeric not null,
    order_id uuid not null,
    water_type_id uuid null,
    constraint order_items_pkey primary key (order_items_id),
    constraint order_items_order_id_fkey foreign key (order_id) references orders (order_id) on update cascade on delete cascade,
    constraint order_items_water_type_id_fkey foreign key (water_type_id) references water_type (id) on update restrict on delete restrict
  ) tablespace pg_default;


The cart will be used in the statement inside the
 if (customerId !== undefined) {
where I will also save the required data for the order table
try{
       //rest of the data

        const {data: customerData, error: customerError} = await supabase.from('customers')
            .upsert({
                //rest of the data
            }).select()
        
            const customerId = customerData?.[0]?.customer_id;

            // Check if customerId is defined before using it
            if (customerId !== undefined) {
                // Proceed with further processing
                const water_refilling_station_id = formData.get('water_refilling_station_id')
                console.log(water_refilling_station_id, "id of water station")
            } else {
                // Handle the case when customerId is undefined
                return { message: "CustomerId is not available." };
            }

    }catch(e){
        return {message: "Failed to submit the form."}
    }
Answered by Barbary Lion
When you use bind, it automatically makes you get 2 arguments. I fyou weren't using formState, you'd do it as follow:

On the client component:
<form action={updateCart.bind(null, cart)}>

On the server component:
export async function updateCart(cart, formData) {
View full answer

27 Replies

Barbary Lion
You can use the bind method when calling your server action function.

<form action={ serverActionFn.bind(null, cartData) }> (...)

Further explanation on the NextJS documentation:
https://nextjs.org/docs/app/api-reference/functions/server-actions#binding-arguments

I hope that helps! 🙂
ts <form action={formAction.bind(cart)}>
`
This showed an error on the addCustomerOrder : No overload matches this call.
Overload 1 of 2, '(action: (state: any) => Promise<any>, initialState: any, permalink?: string | undefined): [state: any, dispatch: () => void]', gave the following error.
    Argument of type '(prevState: any, formData: FormData, cart: CartItemType[]) => Promise<{ message: string; }>' is not assignable to parameter of type '(state: any) => Promise<any>'.
      Target signature provides too few arguments. Expected 3 or more, but got 1.
  Overload 2 of 2, '(action: (state: any, payload: FormData) => Promise<any>, initialState: any, permalink?: string | undefined): [state: any, dispatch: (payload: FormData) => void]', gave the following error.
    Argument of type '(prevState: any, formData: FormData, cart: CartItemType[]) => Promise<{ message: string; }>' is not assignable to parameter of type '(state: any, payload: FormData) => Promise<any>'.
      Target signature provides too few arguments. Expected 3 or more, but got 2.
on the serveraction:
export default async function addCustomerOrder(prevState: any, formData: FormData, cart: CartItemType[]):
Barbary Lion
As you're using useFormState, I think you should try the following:

const [state, formAction] = useFormState(addCustomerOrder.bind(null, cart), initialState);

And when instantiating the form do it as:
<form action={formAction}>

Can you please check if that will work?
This is wat the error shows
No overload matches this call.
  Overload 1 of 2, '(action: (state: any) => Promise<any>, initialState: any, permalink?: string | undefined): [state: any, dispatch: () => void]', gave the following error.
    Argument of type '(prevState: any, formData: FormData, cart: CartItemType[]) => Promise<{ message: string; }>' is not assignable to parameter of type '(state: any) => Promise<any>'.
      Target signature provides too few arguments. Expected 3 or more, but got 1.
  Overload 2 of 2, '(action: (state: any, payload: FormData) => Promise<any>, initialState: any, permalink?: string | undefined): [state: any, dispatch: (payload: FormData) => void]', gave the following error.
    Argument of type '(prevState: any, formData: FormData, cart: CartItemType[]) => Promise<{ message: string; }>' is not assignable to parameter of type '(state: any, payload: FormData) => Promise<any>'.
      Target signature provides too few arguments. Expected 3 or more, but got 2.
Barbary Lion
I think you've forgotten to set the 'null' parameter inside the bind method
The bind method needs 2 arguments to be passed. The first one should be null in this case.
I tried with this;
and the error:
No overload matches this call.
  Overload 1 of 2, '(action: (state: FormData) => Promise<FormData>, initialState: FormData, permalink?: string | undefined): [state: FormData, dispatch: () => void]', gave the following error.
    Argument of type '(formData: FormData, cart: CartItemType[]) => Promise<{ message: string; }>' is not assignable to parameter of type '(state: FormData) => Promise<FormData>'.
      Target signature provides too few arguments. Expected 2 or more, but got 1.
  Overload 2 of 2, '(action: (state: FormData, payload: CartItemType[]) => Promise<FormData>, initialState: FormData, permalink?: string | undefined): [state: ...]', gave the following error.
    Argument of type '(formData: FormData, cart: CartItemType[]) => Promise<{ message: string; }>' is not assignable to parameter of type '(state: FormData, payload: CartItemType[]) => Promise<FormData>'.
Barbary Lion
Can you show the code for your addCustomerOrder function?
export default async function addCustomerOrder(prevState: any, formData: FormData, cart: CartItemType[]): Promise<{ message: string }> {    
    const cookieStore = cookies()
    const supabase = createServerActionClient({ cookies: () => cookieStore })
    try{
        //rest of the data

        const {data: customerData, error: customerError} = await supabase.from('customers')
            .upsert({
               //data
            }).select()
        
            const customerId = customerData?.[0]?.customer_id;

            // Check if customerId is defined before using it
            if (customerId !== undefined) {
                // Proceed with further processing
                const water_refilling_station_id = formData.get('refilling_station_id')
                console.log(water_refilling_station_id, "id of water station")
                const {data: orderData, error: orderError} = await supabase.from('orders')
                    .upsert({
                        //rest of the data
                    }).select()

                console.log(orderError, "order data")
                
                const order_id = orderData?.[0]?.order_id;

                const {data: orderItems, error: orderItemsError} = await supabase.from('order_items')
                    .upsert({   
                        quantity: 2,
                        order_id,
                        water_type_id : "70e2e497-37b0-4294-a3b3-94bbd95c989a"
                    })
                
                console.log(orderItemsError, "order items error")
            } else {
                // Handle the case when customerId is undefined
                return { message: "CustomerId is not available." };
            }
            
            

        // revalidatePath('/water_station')
        return { message: `Succesfully added the data` }
    }catch(e){
        return {message: "Failed to submit the form."}
    }

}
Barbary Lion
Oh, you're expecting 3 arguments and only passing 2.
Barbary Lion
When you use bind, it automatically makes you get 2 arguments. I fyou weren't using formState, you'd do it as follow:

On the client component:
<form action={updateCart.bind(null, cart)}>

On the server component:
export async function updateCart(cart, formData) {
Answer
Barbary Lion
As you're using formState, you could try expecting only 2 arguments (prevState, and formData) and seeing if that cart one gets injected in the same way as it does when not using formState
Thanks, it works now. I even tried with the prevState and it does retrieve the data of the cart
Barbary Lion
Amazing, those are great news!
You should mark this one as answered, so that it may help others!
How can I set this as solved?
This is all I see on the tags
@Barbary Lion As you're using formState, you could try expecting only 2 arguments (prevState, and formData) and seeing if that cart one gets injected in the same way as it does when not using formState
I just want to ask though, how would this work if you would like to pass on a message for successful and error ones. From before,
const [state, formAction] = useFormState(addCustomerOrder, initialState);
, I was using the state.message
Barbary Lion
On the server action, you'd return something like {status: 'success', message: 'Cart updated'}
On the client component, you could use useEffect to watch the state.message and update something (an alert, toast, whatever) upon getting that state update