Appearance
探索原生 HTML 表单验证
在前端开发中,原生 HTML 表单验证提供了丰富的功能,但常常被忽视。很多开发者更习惯使用 JavaScript 来实现复杂的表单验证逻辑,然而原生的 HTML 表单验证机制能帮我们实现许多常见的需求。在本文中,我们将深入了解这些验证机制,讨论它们的优缺点,并展示一些实用的代码示例。
基础
HTML5 表单验证提供了多种验证机制,例如 required 属性、type 属性和 pattern 属性等。这些属性允许我们为输入字段设置约束条件,浏览器会在提交表单之前自动检查是否满足这些条件。如果某个字段不符合要求,浏览器会自动显示一条错误提示信息。
html
<form>
<input type="text" name="username" required placeholder="请输入用户名" />
<input type="email" name="email" required placeholder="请输入邮箱" />
<input type="submit" value="提交" />
</form>
<form>
<input type="text" name="username" required placeholder="请输入用户名" />
<input type="email" name="email" required placeholder="请输入邮箱" />
<input type="submit" value="提交" />
</form>
在这个示例中,required 属性确保用户名和邮箱字段不能为空,type="email" 则强制邮箱字段必须符合邮箱格式。如果用户未填写用户名或邮箱格式不正确,浏览器会自动阻止表单提交并显示错误信息。
自定义验证信息:setCustomValidity
有时,我们需要在默认验证之外添加自定义的错误提示。例如,我们希望在用户未输入内容时显示自定义的提示信息。此时可以使用 setCustomValidity 方法。
html
<input
name="example"
placeholder="请输入内容"
onChange={(event) => {
const input = event.currentTarget;
if (input.value === "") {
input.setCustomValidity("自定义提示:此字段不能为空");
} else {
input.setCustomValidity("");
}
}}
/>
<input
name="example"
placeholder="请输入内容"
onChange={(event) => {
const input = event.currentTarget;
if (input.value === "") {
input.setCustomValidity("自定义提示:此字段不能为空");
} else {
input.setCustomValidity("");
}
}}
/>
在这个例子中,我们通过监听 onChange 事件,在输入为空时调用 setCustomValidity 方法并设置自定义提示信息。当输入非空时,清空提示信息。
但setCustomValidity 存在一些不足。当输入字段最初是有效的,但在重置后为空时,按下提交按钮不会触发验证提示,导致用户体验不一致。
为了解决这些问题,我们可以创建一个自定义的验证组件,将验证逻辑封装起来,避免重复的代码和不一致的用户体验。
优化验证逻辑:自定义组件
为了解决上述问题,我们可以自定义封装 Input 组件。通过创建一个自定义 Input 组件,我们可以将验证逻辑集中到一起,减少重复代码,提高代码的可读性和可维护性。
js
import { useRef, useLayoutEffect } from "react";
function Input({ customValidity, ...props }) {
const ref = useRef();
useLayoutEffect(() => {
if (customValidity != null) {
const input = ref.current;
input.setCustomValidity(customValidity);
}
}, [customValidity]);
return <input ref={ref} {...props} />;
}
function Form() {
const [value, setValue] = useState("");
return (
<form>
<Input
name="example"
value={value}
onChange={(event) => setValue(event.target.value)}
customValidity={value.length ? "" : "自定义提示:此字段不能为空"}
/>
<button>提交</button>
</form>
);
}
import { useRef, useLayoutEffect } from "react";
function Input({ customValidity, ...props }) {
const ref = useRef();
useLayoutEffect(() => {
if (customValidity != null) {
const input = ref.current;
input.setCustomValidity(customValidity);
}
}, [customValidity]);
return <input ref={ref} {...props} />;
}
function Form() {
const [value, setValue] = useState("");
return (
<form>
<Input
name="example"
value={value}
onChange={(event) => setValue(event.target.value)}
customValidity={value.length ? "" : "自定义提示:此字段不能为空"}
/>
<button>提交</button>
</form>
);
}
通过这种方式,我们实现了一个具备自定义验证能力的输入组件。Input 组件会接收 customValidity 属性并在组件更新时自动执行验证。
复杂验证逻辑示例
在实际应用中,验证逻辑往往比简单的本地检查更复杂,甚至可能涉及异步验证。例如,我们需要检查用户名是否已被使用。这时可以结合 React Query 等库管理验证状态。
js
import { useState } from "react";
import { useQuery } from "@tanstack/react-query";
import { verifyUsername } from "./verifyUsername";
import { Input } from "./Input";
function Form() {
const [value, setValue] = useState("");
const { data, isLoading, isError } = useQuery({
queryKey: ["verifyUsername", value],
queryFn: () => verifyUsername(value),
enabled: Boolean(value),
});
const validationMessage = data?.validationMessage;
return (
<form>
<Input
name="username"
required={true}
customValidity={
isLoading
? "正在验证用户名..."
: isError
? "验证失败"
: validationMessage
}
value={value}
onChange={(event) => {
setValue(event.currentTarget.value);
}}
/>
<button>提交</button>
</form>
);
}
import { useState } from "react";
import { useQuery } from "@tanstack/react-query";
import { verifyUsername } from "./verifyUsername";
import { Input } from "./Input";
function Form() {
const [value, setValue] = useState("");
const { data, isLoading, isError } = useQuery({
queryKey: ["verifyUsername", value],
queryFn: () => verifyUsername(value),
enabled: Boolean(value),
});
const validationMessage = data?.validationMessage;
return (
<form>
<Input
name="username"
required={true}
customValidity={
isLoading
? "正在验证用户名..."
: isError
? "验证失败"
: validationMessage
}
value={value}
onChange={(event) => {
setValue(event.currentTarget.value);
}}
/>
<button>提交</button>
</form>
);
}
再让我们实现一个需要再次确认密码的表单验证逻辑:
js
import { useState } from "react";
import { Input } from "./Input";
function ConfirmPasswordForm() {
const [password, setPassword] = useState("");
const [confirmedPass, setConfirmedPass] = useState("");
const matches = confirmedPass === password;
return (
<form>
<Input
type="password"
name="password"
required={true}
value={password}
onChange={(event) => {
setPassword(event.currentTarget.value);
}}
/>
<Input
type="password"
name="confirmedPassword"
required={true}
value={confirmedPass}
customValidity={matches ? "" : "Password must match"}
onChange={(event) => {
setConfirmedPass(event.currentTarget.value);
}}
/>
<button>Submit</button>
</form>
);
}
import { useState } from "react";
import { Input } from "./Input";
function ConfirmPasswordForm() {
const [password, setPassword] = useState("");
const [confirmedPass, setConfirmedPass] = useState("");
const matches = confirmedPass === password;
return (
<form>
<Input
type="password"
name="password"
required={true}
value={password}
onChange={(event) => {
setPassword(event.currentTarget.value);
}}
/>
<Input
type="password"
name="confirmedPassword"
required={true}
value={confirmedPass}
customValidity={matches ? "" : "Password must match"}
onChange={(event) => {
setConfirmedPass(event.currentTarget.value);
}}
/>
<button>Submit</button>
</form>
);
}
总结
通过自定义的 <Input />
组件,我们能够在 React 中实现一个更可靠、易于管理的验证方案。此组件在初始化和重置表单时自动处理验证逻辑,提升了用户体验和代码可维护性。希望这个方法能为你的项目带来更好的验证体验!