React中如何将表单值存入数组?提交时遇undefined错误
React表单提交添加数组条目报错undefined的解决方案
问题核心原因
- 直接读取
e.target.value错误:<form>元素本身没有value属性,所以e.target.value返回undefined,导致添加到数组的是无效值 - 未正确收集表单字段:没有获取title、message等输入框的内容,无法生成符合现有数组结构的对象
- 表单控件缺少必要属性:隐藏输入框和可见输入框都没有
name属性,无法被FormData识别;字段名不统一(原数组用messages,表单id是message)
修复方案1:使用FormData快速收集表单数据(轻量便捷)
import { useState } from 'react'; const ForumTopic = [ { title: "First Post", messages: "test", author: "Dagger", count: 1, date: "02/16", }, ]; function TopicManager() { const [topic, setTopic] = useState(ForumTopic); const addTopic = (e) => { e.preventDefault(); // 用FormData一次性获取所有表单字段 const formData = new FormData(e.target); // 生成符合数组结构的新对象 const newTopic = { title: formData.get('title'), messages: formData.get('message'), // 统一字段名:message -> messages author: formData.get('author'), count: parseInt(formData.get('count')), // 转换为数字类型 date: formData.get('date').slice(0, 5) // 匹配原数组的MM/DD格式 }; // 用函数式更新避免闭包导致的旧state问题 setTopic(prevTopics => [...prevTopics, newTopic]); // 重置表单 e.target.reset(); }; return ( <div> {/* 渲染现有话题列表 */} <div className="topic-list"> {topic.map((item, idx) => ( <div key={idx} className="topic-item"> <h3>{item.title}</h3> <p>{item.messages}</p> <p>作者:{item.author} | 回复数:{item.count} | 日期:{item.date}</p> </div> ))} </div> <form onSubmit={addTopic}> <div> Create a topic title <label htmlFor="title"> <input id="title" name="title" required /> {/* 必须加name属性 */} </label> </div> <div> Write your message <label htmlFor="message"> <textarea id="message" name="message" required></textarea> {/* 必须加name属性 */} </label> </div> {/* 隐藏输入框也要加name,FormData才能获取值 */} <input id="author" name="author" defaultValue="Dagger" hidden /> <input id="count" name="count" defaultValue="1" hidden /> <input id="date" name="date" defaultValue={new Date().toLocaleDateString()} hidden /> <button type="submit">Post New Message</button> </form> </div> ); } export default TopicManager;
修复方案2:使用受控组件(符合React数据流习惯)
import { useState } from 'react'; const ForumTopic = [ { title: "First Post", messages: "test", author: "Dagger", count: 1, date: "02/16", }, ]; function TopicManager() { const [topic, setTopic] = useState(ForumTopic); // 受控组件状态,统一管理表单数据 const [formState, setFormState] = useState({ title: '', message: '', author: 'Dagger', count: 1, date: new Date().toLocaleDateString().slice(0,5) }); // 统一处理输入变化 const handleInputChange = (e) => { const { name, value } = e.target; setFormState(prev => ({ ...prev, [name]: name === 'count' ? parseInt(value) : value })); }; const addTopic = (e) => { e.preventDefault(); // 转换为数组需要的结构 const newTopic = { ...formState, messages: formState.message }; // 函数式更新state setTopic(prevTopics => [...prevTopics, newTopic]); // 重置表单状态 setFormState({ title: '', message: '', author: 'Dagger', count: 1, date: new Date().toLocaleDateString().slice(0,5) }); }; return ( <div> <div className="topic-list"> {topic.map((item, idx) => ( <div key={idx} className="topic-item"> <h3>{item.title}</h3> <p>{item.messages}</p> <p>作者:{item.author} | 回复数:{item.count} | 日期:{item.date}</p> </div> ))} </div> <form onSubmit={addTopic}> <div> Create a topic title <label htmlFor="title"> <input id="title" name="title" value={formState.title} onChange={handleInputChange} required /> </label> </div> <div> Write your message <label htmlFor="message"> <textarea id="message" name="message" value={formState.message} onChange={handleInputChange} required ></textarea> </label> </div> {/* 受控组件不需要隐藏输入框,直接用state值即可 */} <button type="submit">Post New Message</button> </form> </div> ); } export default TopicManager;
关键修复点总结
- 移除
e.target.value:表单元素无value属性,改用FormData或受控组件收集字段 - 给所有表单控件加
name属性:FormData和受控组件都依赖name来关联数据 - 统一字段名:原数组用
messages,表单输入框对应调整(或转换) - 函数式更新state:
setTopic(prev => [...prev, newItem])避免闭包导致的旧state问题 - 类型转换:count是数字类型,需将表单字符串转为数字
- 日期格式匹配:原数组是MM/DD格式,截取日期字符串对应部分
内容的提问来源于stack exchange,提问作者Dagger
相关产品推荐
相关产品推荐

