likes
comments
collection
share

React 和 FormData当你学习如何在 React 中访问表单数据时,过去你会学习受控字段和非受控字段。稍后你可

作者站长头像
站长
· 阅读数 57

当你学习如何在 React 中访问表单数据时,过去你会学习受控字段和非受控字段。稍后你可能会开始使用第三方抽象,如 Formik 或 React Hook Form,它们在底层使用受控或非受控技术。无论哪种方式,最终目标都是收集表单的数据。对于受控,你的数据就是你的状态。对于非受控,你需要自己收集表单值,通常开发人员会为此选择 ref:

function onSubmit(event: React.FormEvent) {
  event.preventDefault()

  // Collect uncontrolled form fields with refs. The refs are
  // giving us direct access to the input fields in the DOM
  const formValues = {
    name: nameRef.current.value
      email: emailRef.current.value
}
}

React 中的所有表单字段都必须是受控的或不受控制的,因为您要么添加 prop,要么不添加。 ,自 2010 年以来 JavaScript 标准value是一种访问表单数据的方式,无论它是受控的还是不受控制的,但大多数人更喜欢不受控制的FormData

尽管FormData从一开始就可以与 React 一起使用,但我们看到它在过去几年中人气飙升。稍后,我们将向您展示它是如何被现代 React 19 功能采用和推动的。

表单数据

使用FormData,你不需要引用来获取不受控制的表单的值。相反,你可以直接从 读取表单值event.target

function onSubmit(event: React.FormEvent) {
  event.preventDefault()
  const formData = new FormData(event.target)

  const formValues = {
    name: formData.get('name')
    email: formData.get('email)
  }
}

由于某些原因,如果您使用 ,TypeScript 会抱怨,event.target并希望您使用event.currentTarget。不过,您要知道,这两个通常可以指代同一件事,而且大多数情况下,使用哪一个并不重要。但currentTarget现在我们将使用事件。因为许多 React 开发人员都在使用 TS。

添加姓名

请务必在输入字段中添加名称才能正常FormData工作:

// ✅ Works because input has matching name
const email = formData.get('email')
<input type="text" name="email" />

不使用 getter 访问数据

我们不能只做这样的事情来提取所有表单数据而不使用 getter 吗?

// ❌ Wont work
const formValues = { ...formData };

对象实例formData更加不透明,并且与我们可以与对象文字混合的对象类型不同。如果我们使用console.log它,我们也不会看到值:

console.log(formData); // output: `FormData {} [[Prototype]]: FormData`

Object.fromEntries()

您可以避免使用 getter 并将值解包为更简单的对象,如下所示:

const formValues = Object.fromEntries(formData);
console.log(formValues); // output: { name: 'my name', email: 'name@someemail.com' }

但是,使用 TypeScript 这样做并不能提供您想要的类型。

FormData 和 TypeScript 的问题

尽管输入字段的值是字符串,并且如果用户未输入任何值,它将是一个空字符串,但 TypeScript 会表示从 getter 返回的类型是FormDataEntryValue | null

const quantity = formData.get('quantity');
typeof quantity; // FormDataEntryValue | null

这可能会导致很多混乱的工作,例如将用户的输入转换为数字:

// Assert string or null
const quantity = formData.get('quantity') as string | null

// Then provide default incase falsy null value is returned
const quantity = (formData.get('quantity') as string | null) || 0

// Now we can pass to parseInt to get the integer version of the user's input
const quantity = parseInt((formData.get('quantity') as string | null) || 0)

有了Object.fromEntries它就没那么好了。他们只知道它是一个带有未知数量的字符串键和FormDataEntryValue值的对象:

const formValues = Object.fromEntries(formData);
typeof formValues; // { [k: string]: FormDataEntryValue }

Zod 的问题已修复

Zod是一个基于架构的 JavaScript 验证器,与YupJoi等其他验证器类似。但与其前身不同,Zod 是专门为与 TypeScript 配合使用而编写的。由于这不是 Zod 教程,我们将尽可能简短地介绍 Zod 的强大功能。

这里使用 Zod 的想法是,无论如何您可能都需要验证,为什么不获得没有断言的更好的类型呢?

当您将这个不透明formValues对象传递到模式验证器时,Zod 会根据您编写的模式(此处未显示)对其进行验证,然后还会根据刚刚传递的模式规则以类型安全的方式将数据返回给您:

const formValues = Object.fromEntries(formData); // ❌ TYPE: { [k: string]: FormDataEntryValue }

const results = myFormSchema.safeParse(formValues);
if (results.success) {
  results.data; // ✅ TYPE: { email: string, quantity: number }
  console.log(results.data.email); // name@someemail.com
  console.log(results.data.quantity); // 5
} else {
  // Do what you want with results.errors
}

FormData 和 React 19

现代 React API也鼓励您使用和学习 FormData。在 React 19 中,您可以省略onSubmit以下内容action

function MyForm() {
  function formAction(formData: FormData) {
    // We are given an instance of formData instead of event
  }

  return <form action={formAction}>...</form>;
}

当 React 调用你的formAction函数时,它们会向你传递一个实例。我们在 React 19 hooks 中FormData看到了类似的用法,例如useActionStateFormData

框架中使用

Remix即将被整合到 React Router 7 中FormData,它以采用、Request和等 Web 标准而闻名Response。提交表单时,其数据可通过标准Request实例在服务器上获取。根据 MDN 的说法,你可以从FormData下单request

这种处理表单数据的“Remix”方式符合 JavaScript 标准:

// In Remix, the action "catches" POST/PUT/DELETE requests
export async function action(request: Request) {
  const formData = await request.formData();
  // ...
}
转载自:https://juejin.cn/post/7416209918111367204
评论
请登录