React Hook表单的服务器端验证

359 阅读3分钟

出于安全考虑,一个表单除了在客户端验证外,还需要在服务器上验证。某些验证规则可能只在服务器上执行--特别是当这些规则依赖于数据库或其他网络服务时。如果这些服务器验证规则之一失败,我们仍然需要通知用户。那么,我们如何在React Hook Form中处理服务器端的验证?让我们来了解一下。

React Hook Form Server-side Validation 一个例子

我们有一个简单的表单,已经使用React Hook Form实现了。该表单捕获了一个用户的名字:

type FormData = {
  name: string;
};
export default function App() {
  const { register, handleSubmit, errors } = useForm<FormData>();
  const submitForm = async (data: FormData) => {
    const result = await postData(data);
  };
  return (
    <div className="app">
      <form onSubmit={handleSubmit(submitForm)}>
        <input
          ref={register({
            required: { value: true, message: "You must enter your name" }
          })}
          type="text"
          name="name"
          placeholder="Enter your name"
        />
        <ErrorSummary errors={errors} />
      </form>
    </div>
  );
}

表单上的name 字段是强制性的。我们使用React Hook Form中的errors 对象和我们在上一篇文章中创建的ErrorSummary 组件,如果这个字段没有被填入,就会输出一个验证错误。

表单提交逻辑调用一个postData 函数,将表单发送到服务器上。下面是我们对postData 的模拟实现:

type postDataResult<T> = {
  success: boolean;
  errors?: { [P in keyof T]?: string[] };
};
const postData = ({
  name,
}: FormData): Promise<postDataResult<
  FormData
>> => {
  return new Promise((resolve) => {
    setTimeout(() => {
      if (name === "Fred") {
        resolve({
          success: false,
          errors: {
            name: ["The name must be unique"],
          },
        });
      } else {
        resolve({ success: true });
      }
    }, 100);
  });
};

这模拟了对服务器的调用。重要的部分是:

  • 如果提交成功,一个包含success 等于true 的对象将被返回到承诺中。
  • 如果服务器上发生了验证错误,它们将以下列格式返回到承诺中的errors 属性中。
{
  fieldName: ["error1", "error2", ... ],
  ...
}

这就是ASP.NET Core中的验证错误格式。

将服务器验证错误整合到React Hook Form中

我们需要在服务器验证错误发生时通知用户。要做到这一点,我们需要将错误添加到React Hook Form中的errors 对象。我们可以使用 setError方法来实现。所以,让我们从useForm 钩子中解构这个:

const {
  register,
  handleSubmit,
  errors,
  setError,} = useForm<FormData>();

React Hook Form的错误格式是。

{
  fieldName: {
    ruleName: error
  },
  ...
}

因此,我们可以遍历服务器验证错误,调用setError 方法,映射到React Hook Form的错误格式。我们可以创建以下实用函数来完成这个任务:

function addServerErrors<T>(
  errors: { [P in keyof T]?: string[] },
  setError: (
    fieldName: keyof T,
    error: { type: string; message: string }
  ) => void
) {
  return Object.keys(errors).forEach((key) => {
    setError(key as keyof T, {
      type: "server",
      message: errors[key as keyof T]!.join(
        ". "
      ),
    });
  });
}

该实用函数是通用的,可以在任何具有这种服务器错误格式的表单上工作。

我们使用一个映射的类型{ [P in keyof T]?: string[] } ,来表示来自服务器的错误。

我们将React Hook Form中的setError 函数传递给我们的实用函数。

我们遍历服务器错误中的键,调用setError ,将规则名称设为 "server",将错误设为该键的所有服务器错误的组合。

我们必须做一些手脚以使TypeScript编译器满意。我们必须断言key 是一个传入类型的键,因为TypeScript推断它是一个字符串。我们还必须提醒TypeScript,这个错误不是null ,也不是undefined ,而是用非空的断言操作符(!)。

然后我们可以在得到服务器的响应后在提交函数中使用这个实用函数:

const submitForm = async (data: FormData) => {
  const result = await postData(data);
  if (!result.success && result.errors) {    addServerErrors(result.errors, setError);  }};

ErrorSummary 摘要现在将显示来自服务器的错误。

Server validation error 很好!

🏃播放代码

结束语

React Hook Form通过其setError 函数使整合服务器端验证变得简单。实现一个通用的实用函数,将服务器的错误格式映射到React Hook Form的格式,这意味着可以在任何与该服务器交互的表单中使用。