说到在React中处理表单,最流行的方法是将输入值存储在状态变量中。遵循这种方法的原因之一是因为毕竟它是React,每个人都倾向于使用它附带的hooks。使用hooks可以解决React中的许多问题,但是在处理表单时是否必需呢?让我们来看看。
使用“States”存在问题
正如我们已经知道的那样,每当组件内的状态变量的值发生变化时,React都会重新渲染组件以匹配其当前状态。虽然在小型应用程序中这不是一个大问题,但随着应用程序规模的增长,它可能导致性能瓶颈。当涉及到表单时,React会尝试在每次输入(状态)发生变化时重新渲染组件。
小提示:我在StackOverflow上找到了一个非常有用的答案,可以用来计算组件渲染的次数。我们也会在我们的代码中使用这个实用函数。
使用Vite创建一个基本的React应用,并在项目创建后清理掉不需要的文件。
代码语言:javascript复制npm create vite@latest my-vue-app --template react
# yarn
yarn create vite my-vue-app --template react
# pnpm
pnpm create vite my-vue-app --template react
让我们创建一个 React 组件(称为 FormWithState ),其中包含一个表单,该表单接受两个输入:电子邮件和密码。我们将使用状态来管理表单输入。
代码语言:javascript复制import { useEffect, useRef, useState } from "react";
import "./Forms.css";
export default function FormWithState() {
// The count will increment by 2 on initial render due to strict mode then by 1 on subsequent renders
const countRef = useRef(0);
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
useEffect(() => {
countRef.current = countRef.current 1;
});
function handleSubmit(e) {
e.preventDefault();
console.log({ email, password });
}
return (
<div className="form-div">
<h2>Form With State</h2>
<form onSubmit={handleSubmit}>
<div className="input-field">
<label htmlFor="email2">Email</label>
<input
id="email2"
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
autoComplete="off"
/>
</div>
<div className="input-field">
<label htmlFor="password2">Password</label>
<input
id="password2"
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
</div>
<button type="submit">Submit</button>
<div>
<p>
The Component Re-Rendered <span>{countRef.current}</span> times
</p>
</div>
</form>
</div>
);
}
将此组件添加到 App 组件中,并打开 http://localhost:5173
正如你所看到的,表单组件大约被渲染了23次,随着输入字段数量的增加,这个数字会逐渐增加。在大多数情况下,表单值仅在表单提交时使用。那么,难道为了两个输入字段就需要重新渲染20多次的组件吗?答案是明确的:不需要!
此外,当输入字段的数量增加时,存储输入值的状态变量的数量也会增加,从而增加了代码库的复杂性。那么,有没有其他方法可以避免重新渲染,同时实现表单的所有功能呢?
使用FormData来处理表单
所以,另一种方法是使用JavaScript的原生 FormData 接口。根据官方文档的描述,创建一个新的 FormData 对象有三种方法。
代码语言:javascript复制new FormData();
new FormData(form);
new FormData(form, submitter);
我们将使用第二种方法,因为我们已经有一个表单。我们只需要将表单元素传递给构造函数,它将自动填充表单值。为了使其工作,我们还需要在 input 标签中添加 name 属性。让我们测试一下这种方法。创建一个组件(比如 FormWithoutState )。
代码语言:javascript复制import { useEffect, useRef } from "react";
export default function FormWithoutState() {
// The count will increment by 2 on initial render due to strict mode then by 1 on subsequent renders
const countRef = useRef(0);
useEffect(() => {
countRef.current = countRef.current 1;
});
function handleSubmit(e) {
e.preventDefault();
const form = new FormData(e.currentTarget);
const email = form.get("email");
const password = form.get("password");
console.log({ email, password });
const body = {};
for (const [key, value] of form.entries()) {
body[key] = value;
}
console.log(body);
// Do Further input validation and submit the form
}
return (
<div className="form-div">
<h2>Form Without State</h2>
<form onSubmit={handleSubmit}>
<div className="input-field">
<label htmlFor="email1">Email</label>
<input id="email1" type="email" name="email" autoComplete="off" />
</div>
<div className="input-field">
<label htmlFor="password1">Password</label>
<input id="password1" type="password" name="password" />
</div>
<button type="submit">Submit</button>
<div>
<p>
The Component Re-Rendered <span>{countRef.current}</span> times
</p>
</div>
</form>
</div>
);
}
在这个组件中,我们根本没有使用 useState hook。相反,我们将 name 属性添加到 input 标签中。一旦用户提交表单,在 handleSubmit 函数中,我们通过 e.currentTarget 提供表单对象来创建 FormData 。然后,我们通过 FormData.entries() 方法迭代获取表单的键和值来构建表单主体。我们可以使用这个对象进行进一步的输入验证和通过 fetch 或 Axios API进行提交。但是,这种方法对组件重新渲染的影响如何呢?让我们来看看。将这个组件添加到 App 组件中,并打开 http://localhost:5173 。
你难道不觉得惊讶吗?这个组件根本没有重新渲染。
使用FormData的优势
- 表单输入值会自动捕获,无需为每个输入字段维护状态变量。
- 使用 FormData 时,API请求体可以很容易地构建,而使用 useState 时,我们需要组装提交的数据。
- 当表单增长时,它消除了引入新的状态变量的需求。
- 处理多个表单时,您可能会发现在组件之间重复使用类似的状态变量,而 FormData 只需几行代码就可以轻松重用。
- FormData 支持的一项功能是它会自动处理动态字段。即,如果您的表单具有动态生成的字段(根据用户输入添加/删除字段),使用 useState 管理它们的状态需要额外处理,而 FormData 会自动处理这些。
结束
您可以在 CodeSandbox 上查看此文章的代码,https://flx2nr.csb.app/,希望你从这篇文章中学到了一些新东西。如果有任何疑问,请留下评论。谢谢!
由于文章内容篇幅有限,今天的内容就分享到这里,文章结尾,我想提醒您,文章的创作不易,如果您喜欢我的分享,请别忘了点赞和转发,让更多有需要的人看到。同时,如果您想获取更多前端技术的知识,欢迎关注我,您的支持将是我分享最大的动力。我会持续输出更多内容,敬请期待。