我将 React 的
useFormState
钩子与 NextJS 一起使用,几乎如官方文档和示例项目中所述:
"use client";
import { serverFunction } from "@/app/actions";
import { useFormState, useFormStatus } from "react-dom";
const initialState = "";
function SubmitButton() {
const { pending } = useFormStatus();
return (
<button type="submit" aria-disabled={pending}>
Add
</button>
);
}
export default function Example() {
const [state, formAction] = useFormState(serverFunction, initialState);
return (
<div>
<form action={formAction}>
<input type="file" name="image" />
<SubmitButton />
<pre>{JSON.stringify(state, null, 2)}</pre>
</form>
</div>
);
}
在
actions.ts
我有:
"use server";
export async function serverFunction(_: unknown, formData: FormData) {
const imgFile = formData.get("image");
// ... some processing
}
但是,此方法不使用静态类型检查来确保客户端上表单提供的数据与服务器期望的数据相对应。我该如何在这个玩具示例中引入它?
(当然,我可以使用像
fp-ts
或zod
这样的库进行运行时验证,但这不是这个问题的目的)。
useFormState
和 <form action={}>
构建于 FormData
之上,它不采用泛型类型参数。所以你运气不好。
如果您想要静态类型并且愿意尝试类似的操作,您可以使用常规服务器操作:
'use client';
import { useState } from 'react';
import { createUser } from './user';
const initialState = {
email: '',
message: '',
};
export default function Signup() {
const [state, setState] = useState(initialState);
return (
<form
onSubmit={(event) => {
event.preventDefault();
void createUser(state).then(setState);
}}
>
<label htmlFor="email">Email</label>
<input
type="email"
name="email"
required
value={state.email}
onChange={(event) => setState({ ...state, email: event.target.value })}
/>
{state.message && <p>{state.message}</p>}
<button type="submit">Sign up</button>
</form>
);
}
用户.ts
'use server';
export type UserFormInput = {
email: string;
};
export async function createUser(input: UserFormInput) {
if (!input.email.includes('@')) {
return {
...input,
message: 'Invalid email address',
};
}
return {
...input,
message: 'User created',
};
}