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

React中两个useState同步变更问题排查及正确初始化咨询

问题原因与解决方案

错误原因

你遇到的问题核心是对象引用共享:

  • 虽然初始化时用[...initialValues]做了数组的浅拷贝,但数组里的每个员工对象还是引用类型,employees和records的初始元素都指向initialValues里的同一个对象实例。
  • 在handleChange函数中,你直接修改了原对象的属性emp[k] = v,因为records里的对象和这个对象是同一个内存引用,所以哪怕没调用setRecords,records的内容也会被同步修改。

正确实现方法

要解决这个问题,需要保证两个state的对象完全独立,同时遵循React状态不可变的原则:

1. 正确初始化state

初始化时要对数组内的对象做拷贝(这里用对象展开语法做浅拷贝足够,因为对象都是单层结构),避免引用共享:

const [employees, setEmployees] = useState([
  ...initialValues.map(item => ({...item})), 
  {...emptyRow}
]);
const [records, setRecords] = useState(initialValues.map(item => ({...item})));

2. 修改状态时遵循不可变性

handleChange里不能直接修改原对象,要创建新的对象副本:

const handleChange = (e, id) => {
  const k = e.target.name;
  const v = e.target.value;
  const updated = employees.map((emp) => {
    if (emp.id === id) {
      // 返回新的对象,不修改原对象
      return {...emp, [k]: v};
    }
    return emp;
  });
  setEmployees(updated);
};

完整修改后的代码

import { useState } from "react";
import "./App.css";

const initialValues = [
  {
    id: 1,
    name: "John",
    position: "Developer",
    salary: 50000,
    isUpdated: false,
  },
  {
    id: 2,
    name: "Jane",
    position: "Designer",
    salary: 60000,
    isUpdated: false,
  },
  {
    id: 3,
    name: "Bob",
    position: "Manager",
    salary: 70000,
    isUpdated: false,
  },
];

const emptyRow = {
  id: 0,
  name: "",
  position: "",
  salary: 0,
  isUpdated: false,
};

const headings = ["Name", "Position", "Salary", "Action"];

const Employee = ({ employee, handleChange, handleClick }) => (
  <tr key={employee.id}>
    <td>
      <input
        type="text"
        id="name"
        name="name"
        onChange={(e) => handleChange(e, employee.id)}
        value={employee.name}
        disabled={employee.id !== 0}
      />
    </td>
    <td>
      <input
        type="text"
        id="position"
        name="position"
        onChange={(e) => handleChange(e, employee.id)}
        value={employee.position}
        disabled={employee.id !== 0}
      />
    </td>
    <td>
      <input
        type="number"
        id="salary"
        name="salary"
        onChange={(e) => handleChange(e, employee.id)}
        value={employee.salary ? employee.salary : ""}
      />
    </td>
    <td>
      <button onClick={() => handleClick(employee.id)}>{employee.id !== 0 ? "Save" : "Add"}</button>
    </td>
  </tr>
);

function App() {
  // 初始化时对对象做拷贝,避免引用共享
  const [employees, setEmployees] = useState([
    ...initialValues.map(item => ({...item})),
    {...emptyRow}
  ]);
  const [records, setRecords] = useState(initialValues.map(item => ({...item})));

  const handleClick = (id) => {
    const foundEmp = employees.find((x) => x.id === id);
    const foundRec = records.find((x) => x.id === id);
    console.log(`------- foundEmp :`, JSON.stringify(foundEmp, null, 2));
    console.log(`------- foundRec :`, JSON.stringify(foundRec, null, 2));
    
    // 这里可以添加保存逻辑,比如更新records
    if (id === 0) {
      // 添加新员工到records
      const newEmp = {...foundEmp, id: Date.now()};
      setRecords([...records, newEmp]);
      // 重置空行
      setEmployees([...employees.map(item => item.id === 0 ? {...emptyRow} : item)]);
    } else {
      // 保存现有员工到records
      setRecords(records.map(item => item.id === id ? {...foundEmp} : item));
    }
  };

  const handleChange = (e, id) => {
    const k = e.target.name;
    const v = e.target.value;
    const updated = employees.map((emp) => {
      if (emp.id === id) {
        // 返回新对象,不修改原对象
        return {...emp, [k]: v};
      }
      return emp;
    });
    setEmployees(updated);
  };

  return (
    <div className="App">
      <form onSubmit={(e) => e.preventDefault()}>
        <table width="100%" data-testid="table">
          <tbody>
            <tr>
              {headings?.map((h, i) => (
                <th key={i}>{h}</th>
              ))}
            </tr>
            {employees.map((employee) => (
              <Employee employee={employee} handleChange={handleChange} handleClick={handleClick} />
            ))}
          </tbody>
        </table>
      </form>
    </div>
  );
}

export default App;

内容的提问来源于stack exchange,提问作者Zoe L

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 09:22:48