在 React 中验证字符串数组
Validating array of strings in React
我有一个包含一系列电子邮件的表单。该数组没有大小限制。我需要检查数组中的每个元素是否为有效电子邮件。用户应该能够向数组添加新元素,并且应该至少有一封电子邮件,每个新元素都应该有一个有效的电子邮件,并且每个电子邮件都应该是唯一的。我希望只有在用户第一次提交表单后才能进行验证。验证电子邮件列表的正确方法应该是什么?
我正在使用 Ant Design 组件并将无效电子邮件的索引列表保留为 invalidArrayIndexes
,以便我可以在每个无效行上显示错误。当我添加一个新元素时,我无法收到所需的消息(“请输入您的电子邮件!”),并且当我添加或删除新元素时,已验证的索引列表变得混乱。我不确定这是否是验证反应中的字符串列表的正确方法。这是我到目前为止所做的:
import { Button, Form, Input } from "antd";
import { useState } from "react";
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
const isValidEmail = (str) => {
return emailRegex.test(str);
};
const MyForm = () => {
const [emails, setEmails] = useState([""]);
const [invalidArrayIndexes, setInvalidArrayIndexes] = useState([]);
const [firstSubmit, setFirstSubmit] = useState(false);
const addEmail = () => {
const updatedEmails = [...emails];
updatedEmails.push("");
setEmails(updatedEmails);
};
const removeEmail = (index) => {
const updatedEmails = [...emails];
updatedEmails.splice(index, 1);
setEmails(updatedEmails);
};
const formSubmitted = () => {
if (!firstSubmit) {
setFirstSubmit(true);
}
const notValidEmails = emails.filter((email) => {
return !isValidEmail(email);
});
const invalidEmailExist = notValidEmails.length > 0;
if (!invalidEmailExist) {
console.log("now submitting");
console.log(emails);
}
};
const valChanged = (e, index) => {
const updatedEmails = [...emails];
updatedEmails[index] = e.target.value;
if (firstSubmit) {
const isValid = isValidEmail(e.target.value);
if (isValid) {
if (invalidArrayIndexes.indexOf(index) > -1) {
const updatedInvalidArrayIndexes = [...invalidArrayIndexes];
updatedInvalidArrayIndexes.splice(
updatedInvalidArrayIndexes.indexOf(index),
1
);
setInvalidArrayIndexes(updatedInvalidArrayIndexes);
}
} else {
if (invalidArrayIndexes.indexOf(index) < 0) {
const updatedInvalidArrayIndexes = [...invalidArrayIndexes];
updatedInvalidArrayIndexes.push(index);
setInvalidArrayIndexes(updatedInvalidArrayIndexes);
}
}
}
setEmails(updatedEmails);
};
const emailList = emails.map((email, index) => {
return (
<Form.Item
key={index}
name="email"
label="email"
rules={[{ required: true, message: "Please enter your email!" }]}
validateStatus={invalidArrayIndexes.includes(index) && "error"}
help={invalidArrayIndexes.includes(index) ? "not a valid email" : " "}
>
<Input
style={{ width: 300 }}
placeholder="enter email"
value={email}
onChange={(e) => valChanged(e, index)}
/>
<Button type="label" onClick={() => removeEmail(index)}>
remove email
</Button>
</Form.Item>
);
});
return (
<div>
{emailList}
<Button type="label" onClick={addEmail}>
add new email
</Button>
<div style={{ marginTop: 20 }}>
<Button type="primary" onClick={formSubmitted}>
send emails
</Button>
</div>
</div>
);
};
export default MyForm;
我推荐使用类似于 yup 的库。
使用 yup,您可以为表单定义架构,如下所示:
const RegisterUserValidationSchema = yup.object().shape({
name: yup.string()
.min(2, "No way,too short!")
.max(200, "So long,are you serious?")
.required("Required"),
email: yup.string().email("Please enter an email").required("Required"),
password: yup.string().required("Password is required"),
confirmPassword: yup.string()
.oneOf([yup.ref('password'), null], "Passwords must match"),
phoneNumber: yup.number("Please enter a valid phone number"),
})
然后,您可以将您的值与架构进行比较
您看不到所需错误消息的原因是,当您的元素被 Form 组件包裹并在提交表单后,ant design 的验证规则起作用。但是,这不会解决您的问题,因为这种用法仅支持单个表单项,每个表单项都具有唯一的 name
。
我建议您使用 React 表单验证库 react-validatable-form 来验证您的字符串列表,因为它很好地抽象了验证工作流和来自 DOM 元素绑定的验证结果。首先,您应该使用 ReactValidatableFormProvider 包装您的应用程序,例如:
import { ReactValidatableFormProvider } from "react-validatable-form";
import "antd/dist/antd.css";
import MyForm from "./MyForm";
export default function App() {
return (
<ReactValidatableFormProvider>
<MyForm />
</ReactValidatableFormProvider>
);
}
然后你可以使用 useValidatableForm
钩子与规则集 listPath
像:
import { Button, Form, Input } from "antd";
import { useValidatableForm } from "react-validatable-form";
import get from "lodash.get";
const initialFormData = {
emails: [""]
};
const rules = [
{ listPath: "emails", ruleSet: [{ rule: "required" }, { rule: "email" }] }
];
const MyForm = () => {
const {
isValid,
validationError,
formData,
setPathValue,
setFormIsSubmitted
} = useValidatableForm({
rules,
initialFormData,
hideBeforeSubmit: true
});
const addEmail = () => {
const updatedEmails = get(formData, "emails");
updatedEmails.push("");
setPathValue("emails", updatedEmails);
};
const removeEmail = (index) => {
const updatedEmails = get(formData, "emails");
updatedEmails.splice(index, 1);
setPathValue("emails", updatedEmails);
};
const formSubmitted = () => {
setFormIsSubmitted();
if (isValid) {
console.log("now submitting");
console.log(get(formData, "emails"));
}
};
const emailList = get(formData, "emails").map((email, index) => {
return (
<Form.Item
key={index}
validateStatus={get(validationError, `emails{${index}}`) && "error"}
help={get(validationError, `emails{${index}}`) || " "}
>
<Input
style={{ width: 300 }}
placeholder="enter email"
value={get(formData, `emails[${index}]`)}
onChange={(e) => setPathValue(`emails[${index}]`, e.target.value)}
/>
<Button type="label" onClick={() => removeEmail(index)}>
remove email
</Button>
</Form.Item>
);
});
return (
<div>
{emailList}
<Button type="label" onClick={addEmail}>
add new email
</Button>
<div style={{ marginTop: 20 }}>
<Button type="primary" onClick={formSubmitted}>
send emails
</Button>
</div>
</div>
);
};
export default MyForm;
您可以查看 this sandbox 的实际工作示例。
我有一个包含一系列电子邮件的表单。该数组没有大小限制。我需要检查数组中的每个元素是否为有效电子邮件。用户应该能够向数组添加新元素,并且应该至少有一封电子邮件,每个新元素都应该有一个有效的电子邮件,并且每个电子邮件都应该是唯一的。我希望只有在用户第一次提交表单后才能进行验证。验证电子邮件列表的正确方法应该是什么?
我正在使用 Ant Design 组件并将无效电子邮件的索引列表保留为 invalidArrayIndexes
,以便我可以在每个无效行上显示错误。当我添加一个新元素时,我无法收到所需的消息(“请输入您的电子邮件!”),并且当我添加或删除新元素时,已验证的索引列表变得混乱。我不确定这是否是验证反应中的字符串列表的正确方法。这是我到目前为止所做的:
import { Button, Form, Input } from "antd";
import { useState } from "react";
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
const isValidEmail = (str) => {
return emailRegex.test(str);
};
const MyForm = () => {
const [emails, setEmails] = useState([""]);
const [invalidArrayIndexes, setInvalidArrayIndexes] = useState([]);
const [firstSubmit, setFirstSubmit] = useState(false);
const addEmail = () => {
const updatedEmails = [...emails];
updatedEmails.push("");
setEmails(updatedEmails);
};
const removeEmail = (index) => {
const updatedEmails = [...emails];
updatedEmails.splice(index, 1);
setEmails(updatedEmails);
};
const formSubmitted = () => {
if (!firstSubmit) {
setFirstSubmit(true);
}
const notValidEmails = emails.filter((email) => {
return !isValidEmail(email);
});
const invalidEmailExist = notValidEmails.length > 0;
if (!invalidEmailExist) {
console.log("now submitting");
console.log(emails);
}
};
const valChanged = (e, index) => {
const updatedEmails = [...emails];
updatedEmails[index] = e.target.value;
if (firstSubmit) {
const isValid = isValidEmail(e.target.value);
if (isValid) {
if (invalidArrayIndexes.indexOf(index) > -1) {
const updatedInvalidArrayIndexes = [...invalidArrayIndexes];
updatedInvalidArrayIndexes.splice(
updatedInvalidArrayIndexes.indexOf(index),
1
);
setInvalidArrayIndexes(updatedInvalidArrayIndexes);
}
} else {
if (invalidArrayIndexes.indexOf(index) < 0) {
const updatedInvalidArrayIndexes = [...invalidArrayIndexes];
updatedInvalidArrayIndexes.push(index);
setInvalidArrayIndexes(updatedInvalidArrayIndexes);
}
}
}
setEmails(updatedEmails);
};
const emailList = emails.map((email, index) => {
return (
<Form.Item
key={index}
name="email"
label="email"
rules={[{ required: true, message: "Please enter your email!" }]}
validateStatus={invalidArrayIndexes.includes(index) && "error"}
help={invalidArrayIndexes.includes(index) ? "not a valid email" : " "}
>
<Input
style={{ width: 300 }}
placeholder="enter email"
value={email}
onChange={(e) => valChanged(e, index)}
/>
<Button type="label" onClick={() => removeEmail(index)}>
remove email
</Button>
</Form.Item>
);
});
return (
<div>
{emailList}
<Button type="label" onClick={addEmail}>
add new email
</Button>
<div style={{ marginTop: 20 }}>
<Button type="primary" onClick={formSubmitted}>
send emails
</Button>
</div>
</div>
);
};
export default MyForm;
我推荐使用类似于 yup 的库。 使用 yup,您可以为表单定义架构,如下所示:
const RegisterUserValidationSchema = yup.object().shape({
name: yup.string()
.min(2, "No way,too short!")
.max(200, "So long,are you serious?")
.required("Required"),
email: yup.string().email("Please enter an email").required("Required"),
password: yup.string().required("Password is required"),
confirmPassword: yup.string()
.oneOf([yup.ref('password'), null], "Passwords must match"),
phoneNumber: yup.number("Please enter a valid phone number"),
})
然后,您可以将您的值与架构进行比较
您看不到所需错误消息的原因是,当您的元素被 Form 组件包裹并在提交表单后,ant design 的验证规则起作用。但是,这不会解决您的问题,因为这种用法仅支持单个表单项,每个表单项都具有唯一的 name
。
我建议您使用 React 表单验证库 react-validatable-form 来验证您的字符串列表,因为它很好地抽象了验证工作流和来自 DOM 元素绑定的验证结果。首先,您应该使用 ReactValidatableFormProvider 包装您的应用程序,例如:
import { ReactValidatableFormProvider } from "react-validatable-form";
import "antd/dist/antd.css";
import MyForm from "./MyForm";
export default function App() {
return (
<ReactValidatableFormProvider>
<MyForm />
</ReactValidatableFormProvider>
);
}
然后你可以使用 useValidatableForm
钩子与规则集 listPath
像:
import { Button, Form, Input } from "antd";
import { useValidatableForm } from "react-validatable-form";
import get from "lodash.get";
const initialFormData = {
emails: [""]
};
const rules = [
{ listPath: "emails", ruleSet: [{ rule: "required" }, { rule: "email" }] }
];
const MyForm = () => {
const {
isValid,
validationError,
formData,
setPathValue,
setFormIsSubmitted
} = useValidatableForm({
rules,
initialFormData,
hideBeforeSubmit: true
});
const addEmail = () => {
const updatedEmails = get(formData, "emails");
updatedEmails.push("");
setPathValue("emails", updatedEmails);
};
const removeEmail = (index) => {
const updatedEmails = get(formData, "emails");
updatedEmails.splice(index, 1);
setPathValue("emails", updatedEmails);
};
const formSubmitted = () => {
setFormIsSubmitted();
if (isValid) {
console.log("now submitting");
console.log(get(formData, "emails"));
}
};
const emailList = get(formData, "emails").map((email, index) => {
return (
<Form.Item
key={index}
validateStatus={get(validationError, `emails{${index}}`) && "error"}
help={get(validationError, `emails{${index}}`) || " "}
>
<Input
style={{ width: 300 }}
placeholder="enter email"
value={get(formData, `emails[${index}]`)}
onChange={(e) => setPathValue(`emails[${index}]`, e.target.value)}
/>
<Button type="label" onClick={() => removeEmail(index)}>
remove email
</Button>
</Form.Item>
);
});
return (
<div>
{emailList}
<Button type="label" onClick={addEmail}>
add new email
</Button>
<div style={{ marginTop: 20 }}>
<Button type="primary" onClick={formSubmitted}>
send emails
</Button>
</div>
</div>
);
};
export default MyForm;
您可以查看 this sandbox 的实际工作示例。