You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 10:09:15