Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TypeScript error using useActionState following NextJS tutorial

I followed the NextJS tutorial a while ago back when it used useFormState instead of useActionState which React has since renamed to. As far as I can tell, it's only been a name change and the syntax is the same.

Have I perhaps got the parameters incorrect?

This is the error, it is red and underlined on hover over useActionState:

No overload matches this call.
  Overload 1 of 2, '(action: (state: { errors: { eventName?: string[] | undefined; eventLocations?: string[] | undefined; eventTypes?: string[] | undefined; eventStartDate?: string[] | undefined; eventEndDate?: string[] | undefined; eventHouses?: string[] | undefined; eventVictors?: string[] | undefined; eventLosers?: string[] | undefined; eventExtraNotes?: string[] | undefined; }; message: string; } | { ...; }) => { ...; } | ... 1 more ... | Promise<...>, initialState: { ...; } | { ...; }, permalink?: string | undefined): [state: ...]', gave the following error.
    Argument of type '(prevState: State, formData: FormData) => Promise<{ errors: { eventName?: string[] | undefined; eventLocations?: string[] | undefined; eventTypes?: string[] | undefined; eventStartDate?: string[] | undefined; ... 4 more ...; eventExtraNotes?: string[] | undefined; }; message: string; } | { ...; }>' is not assignable to parameter of type '(state: { errors: { eventName?: string[] | undefined; eventLocations?: string[] | undefined; eventTypes?: string[] | undefined; eventStartDate?: string[] | undefined; eventEndDate?: string[] | undefined; eventHouses?: string[] | undefined; eventVictors?: string[] | undefined; eventLosers?: string[] | undefined; even...'.
      Target signature provides too few arguments. Expected 2 or more, but got 1.
  Overload 2 of 2, '(action: (state: { errors: { eventName?: string[] | undefined; eventLocations?: string[] | undefined; eventTypes?: string[] | undefined; eventStartDate?: string[] | undefined; eventEndDate?: string[] | undefined; eventHouses?: string[] | undefined; eventVictors?: string[] | undefined; eventLosers?: string[] | undefined; eventExtraNotes?: string[] | undefined; }; message: string; } | { ...; }, payload: FormData) => { ...; } | ... 1 more ... | Promise<...>, initialState: { ...; } | { ...; }, permalink?: string | undefined): [state: ...]', gave the following error.
    Argument of type '{ message: null; errors: {}; }' is not assignable to parameter of type '{ errors: { eventName?: string[] | undefined; eventLocations?: string[] | undefined; eventTypes?: string[] | undefined; eventStartDate?: string[] | undefined; eventEndDate?: string[] | undefined; eventHouses?: string[] | undefined; eventVictors?: string[] | undefined; eventLosers?: string[] | undefined; eventExtraNo...'.
      Type '{ message: null; errors: {}; }' is not assignable to type '{ errors: { eventName?: string[] | undefined; eventLocations?: string[] | undefined; eventTypes?: string[] | undefined; eventStartDate?: string[] | undefined; eventEndDate?: string[] | undefined; eventHouses?: string[] | undefined; eventVictors?: string[] | undefined; eventLosers?: string[] | undefined; eventExtraNo...'.
        Types of property 'message' are incompatible.
          Type 'null' is not assignable to type 'string'.

This is the relevant code:

events-page.tsx

'use client';
import { House, Type, Location } from '@/data/definitions';
import { useActionState, useEffect, useState } from 'react';
import { EventSubmission } from '@/lib/definitions';
import { createEvent } from '@/lib/actions';

export default function EventsForm({
    houses,
    types,
    locations
}: {
    houses: House[];
    types: Type[];
    locations: Location[];
}) {
    const initialState = { message: null, errors: {} };
    const [state, dispatch] = useActionState(createEvent, initialState);

    const [formData, setFormData] = useState<EventSubmission>({
        eventName: '',
        eventLocations: '',
        eventTypes: '',
        eventStartDate: '',
        eventEndDate: '',
        eventHouses: '',
        eventVictors: '',
        eventLosers: '',
        eventExtraNotes: ''
    });

    const handleChange = (e: any) => {
        const { name, value, type } = e.target;
        let valueToSubmit = value;

        if (type === 'date') {
            const [year, month, day] = value.split('-').map((num: string) => parseInt(num, 10));

            if (year && month && day) {
                const formattedYear = year.toString().padStart(4, '0');
                const formattedMonth = (month).toString().padStart(2, '0');
                const formattedDay = day.toString().padStart(2, '0');

                valueToSubmit = `${formattedYear}-${formattedMonth}-${formattedDay}`;
            }
        }
        setFormData({
            ...formData,
            [name]: valueToSubmit
        });

        console.log(formData);
    }

    useEffect(() => {
        console.log(formData);
    }, [formData]);

    return (
        <>
            <div className="admin-intro">
                <h2>Add a new Event</h2>
                <p>Submit a new event to the database.</p>
            </div>
            <form className="admin-form" action={dispatch}>
                <div className="admin-form-main">
                    <div className="admin-input">
                        <label htmlFor="eventName">Event Name</label>
                        <input id="eventName" type="text" name="eventName" placeholder="Event Name..." value={formData.eventName} onChange={handleChange} />
                        {state?.errors?.eventName && state.errors.eventName.map((error) => (
                            <p key={error} className="form__input--error">{error}</p>
                        ))}
                    </div>
                    --- More Inputs ---
                </div>
                <div className="admin-submit">
                    <button type="submit">Submit</button>
                </div>
            </form>
        </>
    )
}

actions.ts

import { EventFormSchema } from "./schemas";

// Events

export type State = {
    errors?: {
        eventName?: string[];
        eventLocation?: string[];
        eventType?: string[];
        eventStartDate?: string[];
        eventEndDate?: string[];
        eventHouses?: string[];
        eventVictors?: string[];
        eventLosers?: string[];
        eventExtraNotes?: string[];
    };
    message?: string | null;
};

export async function createEvent(prevState: State, formData: FormData) {
    console.log(formData);
    const validatedFields = EventFormSchema.safeParse({
        eventName: formData.get('eventName'),
        eventLocation: formData.get('eventLocations'),
        eventType: formData.get('eventTypes'),
        eventStartDate: formData.get('eventStartDate'),
        eventEndDate: formData.get('eventEndDate'),
        eventHouses: formData.get('eventHouses'),
        eventVictors: formData.get('eventVictors'),
        eventLosers: formData.get('eventLosers'),
        eventExtraNotes: formData.get('eventNotes')
    });

    if (!validatedFields.success) {
        return {
            errors: validatedFields.error.flatten().fieldErrors,
            message: 'Missing or incorrect fields. Failed to create event.'
        };
    }

    const { eventName, eventLocations, eventTypes, eventStartDate, eventEndDate, eventHouses, eventVictors, eventLosers, eventExtraNotes } = validatedFields.data;

    console.log(validatedFields.data);

    return {
        message: 'Event created successfully.'
    };
}

schemas.ts

import { z } from 'zod';

export const EventFormSchema = z.object({
    eventName: z.string({
        required_error: "Event name is required."
    }).min(1, "Event name is required."),
    eventLocations: z.string({
        required_error: "Please select a location. If none exists, add a location beforehand."
    }).min(1, "Please select a location. If none exists, add a location beforehand."),
    eventTypes: z.string({
        required_error: "Please select an event type. If none exists, please create one beforehand."
    }).min(1, "Please select an event type. If none exists, please create one beforehand."),
    eventStartDate: z.date().optional().nullable().transform(val => val ?? undefined).refine(val => !val, {
        message: "Event start date must be a valid date string.",
    }),
    eventEndDate: z.date().optional().nullable().transform(val => val ?? undefined).refine(val => !val, {
        message: "Event end date must be a valid date string.",
    }),
    eventHouses: z.union([z.string(), z.array(z.string())]).optional().nullable().transform(val => val ?? undefined).refine(val => {
        if (Array.isArray(val)) return val.every(house => typeof house === 'string');
        return true;
    }, {
        message: "Event houses must be a string or an array of strings.",
    }),
    eventVictors: z.union([z.string(), z.array(z.string())]).optional().nullable().transform(val => val ?? undefined).refine(val => {
        if (Array.isArray(val)) return val.every(victor => typeof victor === 'string');
        return true;
    }, {
        message: "Event victors must be a string or an array of strings.",
    }),
    eventLosers: z.union([z.string(), z.array(z.string())]).optional().nullable().transform(val => val ?? undefined).refine(val => {
        if (Array.isArray(val)) return val.every(loser => typeof loser === 'string');
        return true;
    }, {
        message: "Event losers must be a string or an array of strings.",
    }),
    eventExtraNotes: z.string().optional().nullable().default("")
}).refine(data => {
    if (data.eventStartDate && data.eventEndDate) {
        console.log(data.eventStartDate);
        console.log(data.eventEndDate);
        return new Date(data.eventEndDate) > new Date(data.eventStartDate);
    }
    return true;
}, {
    message: "Event end date must be later than event start date.",
    path: ['eventEndDate'],
});
like image 457
Cathair M Avatar asked May 29 '26 17:05

Cathair M


1 Answers

I have since resolved this. useActionState is only accessible via canary releases of NextJS. I reverted back to useFormState but that gave me the same error. I resolved that with altering the following line:

const initialState = { message: null, errors: {} };

to

const initialState = { message: '', errors: {} };

The message needed to be defined as an empty string as opposed to being null.

like image 86
Cathair M Avatar answered Jun 01 '26 07:06

Cathair M



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!