React JS中嵌套表单字段验证实现方案求助
解决方案:Reactstrap嵌套表单验证(不修改原API数据)
嘿,我完全理解你的困境——不能改动来自API的data对象,还要给每个嵌套的表单字段单独加必填和邮箱正则验证,确实有点绕。不过咱们可以通过组件状态单独管理表单输入和验证状态来解决,完全不碰原数据。下面是完整的可运行方案:
const { Component, Fragment } = React; const { Button, Collapse, Form, FormGroup, Input, FormFeedback } = Reactstrap; // 邮箱正则验证规则 const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; class App extends Component { constructor(props) { super(props); this.state = { formData: [], // 存储每个sublevel项的输入值 errors: [] // 存储每个字段的错误信息 }; } componentDidMount() { // 根据原data结构初始化formData和errors,完全不修改原data const initialFormData = data.map(levelOne => ({ sublevel: levelOne.sublevel.map(subItem => ({ ccoId: subItem.ccoId, email: subItem.email, firstName: subItem.firstName, lastName: subItem.lastName })) })); const initialErrors = data.map(levelOne => ({ sublevel: levelOne.sublevel.map(() => ({ ccoId: '', email: '', firstName: '', lastName: '' })) })); this.setState({ formData: initialFormData, errors: initialErrors }); } // 验证单个字段 validateField(value, fieldName, i, j) { let error = ''; // 必填校验 if (!value.trim()) { error = `${fieldName} 是必填项`; } // 邮箱额外正则校验 else if (fieldName === 'email' && !EMAIL_REGEX.test(value)) { error = '请输入有效的邮箱地址'; } // 更新对应字段的错误状态 this.setState(prevState => { const newErrors = [...prevState.errors]; newErrors[i].sublevel[j][fieldName] = error; return { errors: newErrors }; }); return error === ''; // 返回是否验证通过 } // 处理输入变化 handleChange = (i, j, e) => { const { name, value } = e.target; // 更新输入值 this.setState(prevState => { const newFormData = [...prevState.formData]; newFormData[i].sublevel[j][name] = value; return { formData: newFormData }; }, () => { // 输入更新后触发验证 this.validateField(value, name, i, j); }); } // 检查所有字段是否验证通过 isFormValid() { return this.state.errors.every(levelOne => levelOne.sublevel.every(subItem => Object.values(subItem).every(error => error === '') ) ); } // 提交表单(示例) handleSubmit = () => { if (this.isFormValid()) { console.log('表单验证通过,提交数据:', this.state.formData); // 这里可以调用API提交表单数据 } } render() { const { formData, errors } = this.state; return ( <div className="container mt-4"> {data.map((levelOne, i) => ( <div key={i} className="mb-4"> <h4>{levelOne.title}</h4> {levelOne.sublevel.map((subLevel, j) => ( <div key={j} className="border p-3 mb-2"> <Form inline> <FormGroup className="mr-3"> <Input name="ccoId" maxLength="50" value={formData[i]?.sublevel[j]?.ccoId || ''} placeholder="ccoId" onChange={(e) => this.handleChange(i, j, e)} invalid={!!errors[i]?.sublevel[j]?.ccoId} /> <FormFeedback>{errors[i]?.sublevel[j]?.ccoId}</FormFeedback> </FormGroup> <FormGroup className="mr-3"> <Input name="email" type="email" value={formData[i]?.sublevel[j]?.email || ''} placeholder="Email" onChange={(e) => this.handleChange(i, j, e)} invalid={!!errors[i]?.sublevel[j]?.email} /> <FormFeedback>{errors[i]?.sublevel[j]?.email}</FormFeedback> </FormGroup> <FormGroup className="mr-3"> <Input name="firstName" maxLength="50" value={formData[i]?.sublevel[j]?.firstName || ''} placeholder="firstName" onChange={(e) => this.handleChange(i, j, e)} invalid={!!errors[i]?.sublevel[j]?.firstName} /> <FormFeedback>{errors[i]?.sublevel[j]?.firstName}</FormFeedback> </FormGroup> <FormGroup> <Input name="lastName" maxLength="50" value={formData[i]?.sublevel[j]?.lastName || ''} placeholder="lastName" onChange={(e) => this.handleChange(i, j, e)} invalid={!!errors[i]?.sublevel[j]?.lastName} /> <FormFeedback>{errors[i]?.sublevel[j]?.lastName}</FormFeedback> </FormGroup> </Form> </div> ))} </div> ))} <Button color="secondary" size="lg" onClick={this.handleSubmit} disabled={!this.isFormValid()} > Submit </Button> </div> ); } } // 原API数据(不可修改) const data = [ { "Id":22383, "title":"Title 1", "quantity":5, "price":12, "sublevel":[ { "confirm":3, "status":0, "email":"test12@gmail.com", "ccoId":"test12", "firstName":"test", "lastName":"user", "partId":22383 }, { "confirm":3, "status":0, "email":"", "ccoId":"", "firstName":"", "lastName":"", "partId":22383 }, { "confirm":3, "status":0, "email":"", "ccoId":"", "firstName":"", "lastName":"", "partId":22383 }, { "confirm":3, "status":0, "email":"", "ccoId":"", "firstName":"", "lastName":"", "partId":22383 }, { "confirm":3, "status":0, "email":"", "ccoId":"", "firstName":"", "lastName":"", "partId":22383 } ] }, { "Id":22383, "title":"Title 2", "quantity":1, "price":5, "sublevel":[ { "confirm":3, "status":0, "email":"test23@gmail.com", "ccoId":"test23", "firstName":"hello", "lastName":"world", "partId":22383 } ] } ] ReactDOM.render(<App />, document.getElementById("root"));
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.4.1/css/bootstrap.min.css" /> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/reactstrap/8.4.1/reactstrap.min.js"></script> <div id="root"></div>
关键思路解析:
- 状态分离:用
formData存储用户输入的最新值,errors存储每个字段的验证错误,两者结构完全对应原data,但完全独立,不会修改原API数据。 - 实时验证:每次输入变化时调用
validateField函数,实时更新错误状态,并通过reactstrap的FormFeedback显示错误提示。 - 精准定位:通过一级索引
i和二级索引j,精准找到每个嵌套字段对应的状态,确保验证和输入更新的准确性。 - 整体校验:
isFormValid函数遍历所有错误状态,只有当所有字段都验证通过时,Submit按钮才会启用。
这样既满足了不能修改原data的要求,又完美实现了每个字段的必填验证和邮箱正则验证,而且和reactstrap的表单组件无缝集成~
内容的提问来源于stack exchange,提问作者user12856847
相关产品推荐
相关产品推荐

