React如何追踪状态更新,在列表中渲染所有表单提交数据
问题原因
你现在的代码存不住历史记录,核心是3个逻辑错误:
- 只有存「当前表单输入内容」的状态,没有专门存「所有历史提交记录」的状态:每次输入新内容时,
setData会直接覆盖旧的单条表单值,旧数据根本没有被保存下来 - 提交逻辑位置错误:提交按钮和提交事件写在了负责渲染列表的
List组件里,点击提交时除了阻止默认事件,没有做任何「保存当前输入到历史记录」的操作 - 列表渲染逻辑错误:
List组件每次渲染都会重新创建一个空数组,只把当前传进来的单条表单对象的属性拆进去,没有累加历史数据的能力,组件重渲染时临时创建的数组会直接重置
修复方案
按以下步骤改就可以实现不丢历史记录的效果,都是最基础的React状态用法,很好理解:
1. 调整Forms组件的状态和提交逻辑
- 新增一个数组类型的状态
expenseList,初始值为空数组,专门存所有提交过的收支记录 - 把提交事件移到Forms组件内,提交时把当前表单的输入值追加到
expenseList里,提交后重置表单到初始状态 - 修复下拉选择框的value绑定错误:你定义的字段名是
select,原来写的data.type是不存在的,会导致下拉选择失效
2. 简化List组件逻辑
- 去掉组件内临时创建空数组、拆对象属性的逻辑,直接接收父组件传过来的完整历史记录数组
- 遍历历史记录数组,渲染每一条的内容即可,不需要自己处理数据转换
修复后完整代码
import { useState } from 'react'; // 补上这个工具函数,作用是获取当前日期的YYYY-MM-DD格式值 function getCurrentDate() { return new Date().toISOString().split('T')[0]; } export default function Forms() { // 存当前表单正在输入的单条内容 const [data, setData] = useState({ concept:"", amount:"$", date:getCurrentDate(), select: "Income" }) // 新增:存所有历史提交的记录,初始是空数组 const [expenseList, setExpenseList] = useState([]); function handleChange(event){ let name = event.target.name; let value = event.target.value; setData({...data,[name]:value}) } // 把提交逻辑移到表单组件里 function handleSubmit(event) { event.preventDefault(); // 把当前填好的表单数据追加到历史记录数组里,注意要生成新数组,不要直接改原状态 setExpenseList([...expenseList, {...data}]); // 提交后重置表单,方便下次输入 setData({ concept:"", amount:"$", date:getCurrentDate(), select: "Income" }) } return ( <> <h2>Incomes - Outcomes</h2> <form className="form" onSubmit={handleSubmit}> <label>Concept:</label> <input type="text" name="concept" value={data.concept} onChange={handleChange} /> <label>Amount:</label> <input type="text" name="amount" value={data.amount} onChange={handleChange}/> <label>Date:</label> <input name="date" value={data.date} onChange={handleChange}/> <label>Select type of expense:</label> {/* 修复value绑定错误,原来的data.type不存在,应该绑定data.select */} <select name="select" value={data.select} onChange={handleChange}> <option value="Income">Income</option> <option value="Outcome">Outcome</option> </select> {/* 提交按钮放到form标签内部,用type="submit"触发表单提交 */} <button type="submit">Submit</button> </form> {/* 把存所有历史记录的expenseList传给List组件 */} <List expenseList={expenseList}/> </> ); } export default function List({ expenseList }) { return ( <div> <h3>History Records</h3> {/* 直接遍历传进来的历史记录数组渲染即可 */} {expenseList.map((item, index) =>( <ul key={index}> <li>Concept: {item.concept}</li> <li>Amount: {item.amount}</li> <li>Date: {item.date}</li> <li>Type: {item.select}</li> </ul> ))} </div> ); }
原理解释
你可以把React的状态理解成组件的「记忆」:
- 原来你只给组件留了记「当前正在输什么」的记忆位置,新输入的内容会把旧的挤掉
- 新增的
expenseList就是专门留出来记「所有提交过的内容」的位置,每次点提交就把当前输入的内容存到这个列表里,自然不会丢 - 不要在组件渲染函数里用普通临时变量存重要数据,组件每次更新(比如输入内容触发状态变化)都会重新执行渲染函数,里面的普通变量都会被重新初始化,只有用
useState定义的状态会被React持久保留
内容的提问来源于stack exchange,提问作者ArgentumCoder
相关产品推荐
相关产品推荐

