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

React组件首次点击calculate按钮报Cannot read properties of undefined错误

React setState异步导致首次点击calculate报错的解决方案

错误原因

  • React的setState是异步操作,调用setUsersName和setUsers后,组件状态不会立刻更新
  • 首次点击calculate时,retribute函数里使用的usersNames和users还是更新前的旧状态,导致通过indexOf查找位置后,取出的debtor或creditor为undefined,访问balance时触发TypeError
  • 第二次点击时,状态已经完成更新,所以能正常获取到用户数据

解决方案

直接在calculate函数中传递最新的用户数据给retribute,不要依赖还未更新的组件状态,具体修改如下:

1. 修改calculate函数

const calculate = () => {
  const total = users.reduce((acc, user) => acc + user.amount, 0);
  const perPerson = total / users.length;
  const newUsers = users.map(user => {
    return {...user, balance: user.amount - perPerson, transactions: []};
  });
  const currentUserNames = newUsers.map(user => user.name); // 直接从newUsers获取最新名称列表
  setTotal(total);
  setUsersName(currentUserNames);
  setUsers(newUsers);
  const {debtors, creditors} = distribute(newUsers);
  retribute(newUsers, currentUserNames, debtors, creditors); // 传入最新的用户数据和名称
};

2. 修改retribute函数

const retribute = (currentUsers, currentUserNames, debtorsList, creditorsList) => {
  const usersUpdated = [...currentUsers]; // 复制最新用户数组,遵循React状态不可变原则
  debtorsList.forEach(debtorName => {
    const debtorPos = currentUserNames.indexOf(debtorName);
    const debtor = usersUpdated[debtorPos];
    creditorsList.forEach(creditorName => {
      if (debtor.balance < 0) {
        const creditorPos = currentUserNames.indexOf(creditorName);
        const creditor = usersUpdated[creditorPos];
        if (creditor.balance >= Math.abs(debtor.balance)) {
          creditor.balance -= Math.abs(debtor.balance);
          debtor.transactions.push(`${debtor.name} gives ${creditor.name}: ${Math.abs(debtor.balance)}`);
          debtor.balance = 0;
        } else {
          debtor.balance += creditor.balance;
          debtor.transactions.push(`${debtor.name} gives ${creditor.name}: ${creditor.balance}`);
          creditor.balance = 0;
        }
      }
    });
  });
  console.log(usersUpdated);
  // 若需要将计算结果同步到组件状态,可在此调用setUsers(usersUpdated)
};

额外优化点

  • 用Math.abs()替代*-1,代码可读性更强
  • 复制用户数组避免直接修改原数据,符合React状态不可变的设计理念

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 14:22:33