Overload 1 of 2, '(action: (state: { errors: { image_url?: string[] | undefined; bookid?: string[] | undefined; shelf?: string[] | undefined; title?: string[] | undefined; author?: string[] | undefined; publication_year?: string[] | undefined; ... 4 more ...; total_copies?: 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: { image_url?: string[] | undefined; bookid?: string[] | undefined; shelf?: string[] | undefined; title?: string[] | undefined; ... 6 more ...; total_copies?: string[] | undefined; }; message: string; } | { ...; }>' is not assignable to parameter of type '(state: { errors: { image_url?: string[] | undefined; bookid?: string[] | undefined; shelf?: string[] | undefined; title?: string[] | undefined; author?: string[] | undefined; publication_year?: string[] | undefined; ... 4 more ...; total_copies?: string[] | undefined; }; message: string; } | { ...; }) => { ...; } |...'.
Target signature provides too few arguments. Expected 2 or more, but got 1.
Overload 2 of 2, '(action: (state: { errors: { image_url?: string[] | undefined; bookid?: string[] | undefined; shelf?: string[] | undefined; title?: string[] | undefined; author?: string[] | undefined; publication_year?: string[] | undefined; ... 4 more ...; total_copies?: string[] | undefined; }; message: string; } | { ...; }, payload: FormData) => { ...; } | ... 1 more ... | Promise<...>, initialState: { ...; } | { ...; }, permalink?: string | undefined): [state: ...]', gave the following error.
Argument of type '(prevState: [State], formData: FormData) => Promise<{ errors: { image_url?: string[] | undefined; bookid?: string[] | undefined; shelf?: string[] | undefined; title?: string[] | undefined; ... 6 more ...; total_copies?: string[] | undefined; }; message: string; } | { ...; }>' is not assignable to parameter of type '(state: { errors: { image_url?: string[] | undefined; bookid?: string[] | undefined; shelf?: string[] | undefined; title?: string[] | undefined; author?: string[] | undefined; publication_year?: string[] | undefined; ... 4 more ...; total_copies?: string[] | undefined; }; message: string; } | { ...; }, payload: Form...'.
Types of parameters 'prevState' and 'state' are incompatible.
Type '{ errors: { image_url?: string[] | undefined; bookid?: string[] | undefined; shelf?: string[] | undefined; title?: string[] | undefined; author?: string[] | undefined; publication_year?: string[] | undefined; ... 4 more ...; total_copies?: string[] | undefined; }; message: string; } | { ...; }' is not assignable to type '[State]'.
Type '{ errors: { image_url?: string[] | undefined; bookid?: string[] | undefined; shelf?: string[] | undefined; title?: string[] | undefined; author?: string[] | undefined; publication_year?: string[] | undefined; ... 4 more ...; total_copies?: string[] | undefined; }; message: string; }' is not assignable to type '[State]'.
10 | export default function Form() {
11 | const initialState: State = { message: null, errors: {} };
> 12 | const [State, formAction] = useActionState(createBook, initialState);
| ^
13 | return (
14 | <form action={formAction}>
15 | <div className="rounded-md bg-gray-50 p-4 md:p-6">
ELIFECYCLE Command failed with exit code 1.
// create-form.tsx
"use client";
import { createBook, State } from '@/app/lib/book-actions';
import { useActionState } from 'react';
import Link from 'next/link';
import { bookFormFields } from '@/app/lib/book-form-fields';
import { Button } from '@/app/ui/button';
export default function Form() {
const initialState: State = { message: null, errors: {} };
const [State, formAction] = useActionState(createBook, initialState);
return (
<form action={formAction}>
<div className="rounded-md bg-gray-50 p-4 md:p-6">
{bookFormFields.map((field) => (
<div className="mb-4" key={field.id}>
<label htmlFor={field.id} className="mb-2 block text-sm font-medium">
{field.label}
</label>
<div className="relative mt-2 rounded-md">
{field.type === "checkbox" ? (
<input
id={field.id}
name={field.id}
type="checkbox"
className="peer block h-5 w-5 rounded border-gray-200 text-blue-600 focus:ring-2 focus:ring-blue-200"
aria-describedby={`${field.id}-error`}
/>
) : (
<input
id={field.id}
name={field.id}
type={field.type}
placeholder={field.placeholder}
className="peer block w-full rounded-md border border-gray-200 py-2 px-3 text-sm placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-blue-200"
aria-describedby={`${field.id}-error`}
/>
)}
</div>
<div id={`${field.id}-error`} aria-live="polite" aria-atomic="true">
{State.errors?.[field.id] &&
State.errors[field.id].map((error: string) => (
<p className="mt-2 text-sm text-red-500" key={error}>
{error}
</p>
))}
</div>
</div>
))}
</div>
<div className="mt-6 flex justify-end gap-4">
<Link
href="/dashboard/invoices"
className="flex h-10 items-center rounded-lg bg-gray-100 px-4 text-sm font-medium text-gray-600 transition-colors hover:bg-gray-200"
>
Cancel
</Link>
<Button type="submit">Create Book</Button>
</div>
</form>
);
}
'use server';
import { z } from 'zod';
import { sql } from '@vercel/postgres';
import { revalidatePath } from 'next/cache';
import { redirect } from 'next/navigation';
import { signIn } from '@/auth';
import { AuthError } from 'next-auth';
// Define the schema for form validation using Zod
const FormSchema = z.object({
id: z.string(),
bookid: z.string({
invalid_type_error: "Please enter a library book id",
}),
shelf: z.string({
invalid_type_error: "Please enter a library book shelf",
}),
title: z.string({
invalid_type_error: "Please enter a library book title",
}),
author: z.string({
invalid_type_error: "Please enter a library book author",
}),
publication_year: z.number({
invalid_type_error: "Please enter a library book publication year",
}),
genre: z.string({
invalid_type_error: "Please enter a library book genre",
}),
price: z.coerce.number()
.gt(0, { message: 'Please enter an amount greater than 0.' }),
language: z.string({
invalid_type_error: "Please enter a library book language",
}),
image_url: z.string({
invalid_type_error: "Please enter a library book image URL",
}),
in_stock: z.boolean(),
total_copies: z.number(),
date: z.string(),
});
export async function authenticate(
prevState: { message: string },
formData: FormData,
) {
try {
await signIn('credentials', formData);
} catch (error) {
if (error instanceof AuthError) {
switch (error.type) {
case 'CredentialsSignin':
return 'Invalid credentials.';
default:
return 'Something went wrong.';
}
}
throw error;
}
}
const CreateBook = FormSchema.omit({ id: true, date: true });
const UpdateBook = FormSchema.omit({ id: true, date: true });
// Define the structure for the state
export type State = {
errors?: {
bookid?: string[];
shelf?: string[];
title?: string[];
author?: string[];
publication_year?: number[];
genre?: string[];
price?: number[];
language?: string[];
image_url?: string[];
in_stock?: boolean[];
total_copies?: number[];
};
message?: string | null;
};
export async function createBook(prevState: [State], formData: FormData) {
// Validate form using Zod
const validatedFields = CreateBook.safeParse({
bookid: formData.get('bookid'),
shelf: formData.get('shelf'),
title: formData.get('title'),
author: formData.get('author'),
publication_year: Number(formData.get('publication_year')),
genre: formData.get('genre'),
price: Number(formData.get('price')),
language: formData.get('language'),
image_url: formData.get('image_url'),
in_stock: Boolean(formData.get('in_stock')),
total_copies: Number(formData.get('total_copies')),
});
// If form validation fails, return errors early. Otherwise, continue.
if (!validatedFields.success) {
return {
errors: validatedFields.error.flatten().fieldErrors,
message: 'Missing Fields. Failed to Create Book.',
};
}
// Prepare data for insertion into the database
const { bookid,
shelf,
title,
author,
publication_year,
genre,
price,
language,
image_url,
in_stock,
total_copies } = validatedFields.data;
const date = new Date().toISOString().split('T')[0];
// Insert data into the database
try {
await sql`
INSERT INTO books (
bookid,
shelf,
title,
author,
publication_year,
genre,
price,
language,
image_url,
in_stock,
total_copies)
VALUES (
${bookid},
${shelf},
${title},
${author},
${publication_year},
${genre},
${price},
${language},
${image_url},
${in_stock},
${total_copies})
`;
} catch (error) {
// If a database error occurs, return a more specific error.
return {
message: 'Database Error: Failed to Create Book.',
};
}
// Revalidate the cache for the books page and redirect the user.
revalidatePath('/dashboard/books');
redirect('/dashboard/books');
}
我在构建此 NextJS 14 应用程序时遇到状态错误。
useActionState
状态的类型是State
,因为这是initialState
的类型并且是createBook
的返回类型。因此,这也应该是 createBook
的输入类型,而不是 [State]
。
所以尝试改变
export async function createBook(prevState: [State], formData: FormData) {
到
export async function createBook(prevState: State, formData: FormData) {