React类组件中如何从动态表单获取数据并更新state输出JSON数组
问题核心错误点
handleChange逻辑错误:没有定位到video_data/subvideo_data数组的对应索引项,直接把表单值写到了state根目录,没有存进对应的表单对象里- 表单value绑定路径错误:动态生成的表单值都存在数组对应项中,原有代码直接从state根路径取不存在的字段,导致输入时不会同步显示
- 文件输入框错误绑定value:文件输入框的value是只读属性,强制绑定会抛出异常,需要单独处理
修改后完整代码
import React from "react"; import { Button, Container, Form } from "react-bootstrap"; import { FaTrashAlt } from "react-icons/fa"; class Forms extends React.Component { constructor(props) { super(props); this.state = { video_data: [ { _id: "", book_All_details: "", number_of_chapters: "", chapter: 1, video_title: "", description: "", video_url: null, // 文件类型存File对象,不要存空字符串 video_image: null, releaseDate: "", subvideo_data: [ { _id: "", subchapterid: "", subchapter: 1, subvideo_title: "", subdescription: "", subvideo_url: null, subvideo_image: null, subreleaseDate: "", }, ], }, ], }; // 统一绑定this,不用每次render都bind this.handleChange = this.handleChange.bind(this); this.addTClick = this.addTClick.bind(this); this.removeTClick = this.removeTClick.bind(this); this.addFTClick = this.addFTClick.bind(this); this.removeFormFields = this.removeFormFields.bind(this); this.handleSubmit = this.handleSubmit.bind(this); } // 改造后的handleChange,接收章节索引i、子章节索引idx(非必填)、事件对象e handleChange(i, idx, e) { const { name, value, type, files } = e.target; const video_data = [...this.state.video_data]; // 子章节字段更新 if (idx !== undefined) { const subvideo_data = [...video_data[i].subvideo_data]; subvideo_data[idx][name] = type === 'file' ? files[0] : value; video_data[i].subvideo_data = subvideo_data; } else { // 父章节字段更新 video_data[i][name] = type === 'file' ? files[0] : value; } this.setState({ video_data }); } // 添加子章节 addTClick(i) { const video_data = [...this.state.video_data]; video_data[i].subvideo_data.push({ subvideo_title: "", subdescription: "", subvideo_url: null, subvideo_image: "", }); this.setState({ video_data }); } // 删除子章节 removeTClick(i, idx) { const video_data = [...this.state.video_data]; video_data[i].subvideo_data.splice(idx, 1); this.setState({ video_data }); } // 添加父章节 addFTClick() { const video_data = [...this.state.video_data]; video_data.push({ number_of_chapters: "", chapter: video_data.length + 1, video_title: "", description: "", video_url: null, video_image: null, subvideo_data: [ { subvideo_title: "", subdescription: "", subvideo_url: null, subvideo_image: null }, ], }); this.setState({ video_data }); } // 删除父章节 removeFormFields(i) { const video_data = [...this.state.video_data]; video_data.splice(i, 1); this.setState({ video_data }); } // 提交表单获取JSON数组 handleSubmit(e) { e.preventDefault(); // 这里的video_data就是你要的JSON数组格式数据 const formData = this.state.video_data; console.log("提交的表单数据:", JSON.stringify(formData)); // 后续请求逻辑写在这里 } render() { return ( <Container> <form onSubmit={this.handleSubmit}> {this.state.video_data.map((video_datas, i) => ( <div key={i} className="container" style={{ background: "aqua", margin: '10px 0' }}> <div className="form-group col-sm-4"> <label>Enter Chapter Name:</label> <input type="text" name="number_of_chapters" onChange={(e) => this.handleChange(i, undefined, e)} value={video_datas.number_of_chapters} className="form-control" placeholder="title" /> </div> <div className="row"> <div className="form-group col-sm-6"> <label>Title:</label> <input type="text" onChange={(e) => this.handleChange(i, undefined, e)} value={video_datas.video_title} name="video_title" className="form-control" placeholder="title" /> </div> <div className="form-group col-sm-6"> <label>Description:</label> <input type="text" onChange={(e) => this.handleChange(i, undefined, e)} value={video_datas.description} name="description" className="form-control" placeholder="authors" /> </div> </div> <div className="form-group"> <label>Thambnail image:</label> <input onChange={(e) => this.handleChange(i, undefined, e)} name="video_image" type="file" className="form-control" /> </div> <div className="form-group"> <label>Upload Video:</label> <input onChange={(e) => this.handleChange(i, undefined, e)} name="video_url" type="file" className="form-control pb-4" /> </div> <br /> {video_datas.subvideo_data.map((subvideo_datas, idx) => ( <div className="container bg-success py-2" key={idx}> <div className="main"> <div className="row"> <div className="form-group col-sm-6"> <label>Title:</label> <input type="text" onChange={(e) => this.handleChange(i, idx, e)} value={subvideo_datas.subvideo_title} name="subvideo_title" className="form-control" placeholder="title" /> </div> <div className="form-group col-sm-6"> <label>Description:</label> <input type="text" onChange={(e) => this.handleChange(i, idx, e)} value={subvideo_datas.subdescription} name="subdescription" className="form-control" placeholder="title" /> </div> </div> <div className="form-group"> <label>Thambnail image:</label> <input name="subvideo_image" onChange={(e) => this.handleChange(i, idx, e)} type="file" className="form-control" /> </div> <div className="form-group"> <label>Upload Video:</label> <input name="subvideo_url" onChange={(e) => this.handleChange(i, idx, e)} type="file" className="form-control pb-4" /> </div> <br /> <Button variant="danger" onClick={() => this.removeTClick(i, idx)} > <FaTrashAlt style={{ marginRight: "5px" }} /> Remove </Button> </div> </div> ))} <div className="button-section" style={{ textAlign: "center" }}> <Button variant="link" className="addMoreBtn" onClick={() => this.addTClick(i)} > +Add More </Button> </div> {i ? ( <Button variant="danger" onClick={() => this.removeFormFields(i)} > <FaTrashAlt style={{ marginRight: "5px" }} /> Remove </Button> ) : null} </div> ))} <div className="button-section" style={{ textAlign: "center" }}> <Button className="addMoreBtn" onClick={this.addFTClick}> +Add chapter </Button> {/* 提交按钮 */} <Button type="submit" variant="primary" style={{marginLeft: '10px'}}> 提交获取JSON </Button> </div> </form> </Container> ); } } export default Forms;
效果说明
点击提交按钮后,this.state.video_data就是你需要的JSON数组格式数据,直接打印或者发起接口请求即可。文件类型字段会存储对应的File对象,如果需要转成base64或者FormData格式提交,可自行扩展处理逻辑。
内容的提问来源于stack exchange,提问作者Ashutosh kumar
相关产品推荐
相关产品推荐

