
在React应用中,react-hook-form是一个流行的表单库,它提供了高性能、灵活且易于使用的表单管理能力。结合yup这个强大的JavaScript schema验证库,我们可以轻松实现复杂的客户端表单验证逻辑。
客户端验证的主要目的是在数据发送到服务器之前,对用户输入进行即时检查,例如验证字段是否为空、格式是否正确(如邮箱格式、密码长度等)。这能显著提升用户体验,减少不必要的服务器请求。
以下是一个使用yup定义验证schema的示例,它用于检查用户名和密码是否已填写:
import * as yup from "yup";
// 定义验证 schema
const schema = yup.object({
username: yup.string().required("用户名是必填字段"),
password: yup.string().required("密码是必填字段"), // 这里的“密码不正确”提示仅是针对必填的,并非业务逻辑
}).required();在React组件中,我们可以这样整合react-hook-form和yup:
import React, { useState } from "react";
import { useForm } from "react-hook-form";
import { yupResolver } from "@hookform/resolvers/yup";
import * as yup from "yup";
// ... (schema 定义同上)
function Login() {
const {
handleSubmit,
register,
formState: { errors },
} = useForm({
resolver: yupResolver(schema), // 使用 yupResolver 链接 yup schema
});
const formSubmit = (data) => {
console.log("客户端验证通过的数据:", data);
// 这里将发送数据到服务器
};
return (
<form onSubmit={handleSubmit(formSubmit)}>
<div>
<label htmlFor="username">用户名</label>
<input id="username" type="text" {...register("username")} />
{errors.username && <p style={{ color: 'red' }}>{errors.username.message}</p>}
</div>
<div>
<label htmlFor="password">密码</label>
<input id="password" type="password" {...register("password")} />
{errors.password && <p style={{ color: 'red' }}>{errors.password.message}</p>}
</div>
<button type="submit">登录</button>
</form>
);
}尽管yup在客户端验证方面表现出色,但它无法处理需要与后端数据库进行交互才能确定的验证逻辑。例如,yup可以检查密码是否为空,但它无法判断用户输入的密码是否与数据库中存储的某个账户的密码匹配。这种业务逻辑层面的验证必须在服务器端进行。
当用户尝试登录时,服务器需要验证用户名是否存在、密码是否正确。如果凭据不匹配,服务器会返回一个错误响应。yup无法捕获和显示这类服务器端返回的错误信息,因为它只关注前端定义的schema规则。
为了处理服务器端返回的提交错误(例如“用户名或密码不正确”),我们需要在React组件中引入额外的状态管理机制。核心思路是使用React的useState钩子来存储服务器返回的错误信息,并在UI中进行展示。
在你的React组件中,创建一个新的状态变量来存储服务器端返回的错误信息。
import React, { useState } from "react";
// ... 其他导入
function Login() {
const [submissionError, setSubmissionError] = useState(""); // 用于存储服务器提交错误
// ... 其他状态和 hook在formSubmit函数中,当fetch请求完成并收到服务器响应时,根据响应的状态和内容来设置submissionError。
这里有两种常见的策略:
策略一:解析服务器返回的JSON数据中的错误信息 服务器通常会在错误响应中包含一个JSON对象,其中包含详细的错误消息。
const formSubmit = (data) => {
setSubmissionError(""); // 每次提交前清除之前的错误信息
fetch("http://localhost:3001/login", {
method: "POST",
body: JSON.stringify({ username: data.username, password: data.password }),
headers: { "Content-Type": "application/json" },
})
.then((response) => {
if (response.status === 200) {
// 登录成功
// props.router.navigate("/"); // 导航到其他页面
// setIsLoggedIn(true); // 更新登录状态
console.log("登录成功");
return response.json(); // 如果成功也可能返回数据
} else {
// 登录失败,处理错误响应
return response.json().then((responseData) => {
// 假设服务器返回 { error: "用户名或密码不正确" }
setSubmissionError(responseData.error || "登录失败,请重试。");
throw new Error(responseData.error || "登录失败"); // 抛出错误以便后续catch捕获
});
}
})
.then((responseData) => {
// 处理成功响应数据
console.log("成功响应数据:", responseData);
})
.catch((error) => {
console.error("网络或解析错误:", error);
// 如果错误已经在then块中设置,这里可以不再设置
// 如果是网络错误,可以设置通用的错误信息
if (!submissionError) { // 避免覆盖更具体的服务器错误
setSubmissionError("网络连接错误,请稍后再试。");
}
});
};策略二:根据HTTP状态码自定义错误信息 如果服务器只返回状态码而没有详细的错误JSON,你可以根据状态码在前端自定义错误消息。
const formSubmit = (data) => {
setSubmissionError(""); // 每次提交前清除之前的错误信息
fetch("http://localhost:3001/login", {
method: "POST",
body: JSON.stringify({ username: data.username, password: data.password }),
headers: { "Content-Type": "application/json" },
})
.then((response) => {
if (response.status === 200) {
// 登录成功
// ... 成功处理逻辑
console.log("登录成功");
} else {
// 根据不同的 HTTP 状态码设置不同的错误信息
if (response.status === 401) { // Unauthorized
setSubmissionError("用户名或密码不正确。");
} else if (response.status === 400) { // Bad Request
setSubmissionError("请求参数有误,请检查。");
} else if (response.status === 500) { // Internal Server Error
setSubmissionError("服务器内部错误,请稍后再试。");
} else {
setSubmissionError("登录失败,请重试。");
}
}
})
.catch((error) => {
console.error("Error:", error);
setSubmissionError("网络连接错误,请稍后再试。");
});
};在你的JSX中,条件性地渲染submissionError状态。通常,这个错误会显示在表单的顶部,因为它是一个整体的提交错误,而不是针对某个特定字段的错误。
return (
<div className="login-form">
<h1>登录</h1>
{submissionError && <p style={{ color: 'red', textAlign: 'center' }}>{submissionError}</p>} {/* 显示服务器错误 */}
<form onSubmit={handleSubmit(formSubmit)}>
{/* ... 用户名和密码输入框 */}
<div>
<label htmlFor="username">用户名</label>
<input id="username" type="text" {...register("username")} />
{errors.username && <p style={{ color: 'red' }}>{errors.username.message}</p>}
</div>
<div>
<label htmlFor="password">密码</label>
<input id="password" type="password" {...register("password")} />
{errors.password && <p style={{ color: 'red' }}>{errors.password.message}</p>}
</div>
<button type="submit">登录</button>
</form>
</div>
);将上述修改整合到原始的Login组件中,我们可以得到一个同时处理客户端和服务器端验证的完整示例。
import React, { useState } from "react";
// import Input from "./Input"; // 假设 Input 组件是自定义的,这里简化为原生 input
import { useForm } from "react-hook-form";
import { yupResolver } from "@hookform/resolvers/yup";
import * as yup from "yup";
import { useLocation, useNavigate, useParams } from "react-router-dom";
// import useAuth from "../Components/Zustand - Auth/authLogin"; // 假设存在认证 hook
// 路由 HOC,如果不需要可以移除
function withRouter(Component) {
function ComponentWithRouterProp(props) {
let location = useLocation();
let navigate = useNavigate();
let params = useParams();
return (
<Component
{...props}
router={{ location, navigate, params }}
/>
);
}
return ComponentWithRouterProp;
}
// Yup 验证 schema
const schema = yup.object({
username: yup.string().required("用户名是必填字段"),
password: yup.string().required("密码是必填字段"), // 这里的必填提示与服务器端验证“密码不正确”不同
}).required();
function Login(props) {
// const { isLoggedIn, setIsLoggedIn } = useAuth(); // 假设有认证状态管理
const [submissionError, setSubmissionError] = useState(""); // 新增:服务器提交错误状态
const {
handleSubmit,
register,
formState: { errors },
setError, // react-hook-form 提供的设置字段错误的方法
} = useForm({
resolver: yupResolver(schema),
});
const formSubmit = (data) => {
setSubmissionError(""); // 每次提交前清除之前的服务器错误信息
fetch("http://localhost:3001/login", {
method: "POST",
body: JSON.stringify({ username: data.username, password: data.password }),
headers: { "Content-Type": "application/json" },
})
.then((response) => {
if (response.status === 200) {
// 登录成功
props.router.navigate("/"); // 导航到主页
// setIsLoggedIn(true); // 更新登录状态
console.log("登录成功");
return response.json(); // 解析可能的成功响应数据
} else {
// 登录失败,处理服务器错误
// 尝试解析服务器返回的 JSON 错误信息
return response.json().then((responseData) => {
const errorMessage = responseData.error || "用户名或密码不正确。";
setSubmissionError(errorMessage); // 设置服务器提交错误
// 如果需要将错误关联到特定字段,可以使用 setError
// setError("password", { type: "server", message: errorMessage });
throw new Error(errorMessage); // 抛出错误以便链式调用中断
}).catch(() => {
// 如果服务器返回的不是 JSON 或者解析失败
const genericErrorMessage = "登录失败,请检查您的凭据。";
setSubmissionError(genericErrorMessage);
throw new Error(genericErrorMessage);
});
}
})
.then((responseData) => {
console.log("登录成功响应数据:", responseData);
})
.catch((error) => {
console.error("登录请求发生错误:", error.message);
// 这里的 catch 主要捕获网络错误或上面抛出的错误
if (!submissionError) { // 避免覆盖更具体的服务器错误
setSubmissionError("网络连接错误,请稍后再试。");
}
});
};
return (
<div className="sign-up">
<h1>登录</h1>
{submissionError && <p style={{ color: 'red', textAlign: 'center', marginBottom: '15px' }}>{submissionError}</p>}
<form onSubmit={handleSubmit(formSubmit)}>
{/* 假设 Input 组件接受 register 和 errorMessage prop */}
<div>
<label htmlFor="username">用户名</label>
<input
id="username"
type="text"
placeholder="输入用户名"
{...register("username")}
/>
{errors.username && <p style={{ color: 'red' }}>{errors.username.message}</p>}
</div>
<div>
<label htmlFor="password">密码</label>
<input
id="password"
type="password"
placeholder="输入密码"
{...register("password")}
/>
{errors.password && <p style={{ color: 'red' }}>{errors.password.message}</p>}
</div>
<button type="submit">登录</button>
</form>
<button className="button-link" onClick={() => props.onFormSwitch && props.onFormSwitch("signup")}>
没有账户?点击这里注册。
</button>
</div>
);
}
export default withRouter(Login);通过以上方法,你可以在React应用中有效地结合客户端yup验证和服务器端错误处理,为用户提供健壮且友好的表单体验。
以上就是React表单验证:结合Yup实现客户端校验与处理服务端提交错误的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号