【问题标题】:Unable to set state with useState hook with function call无法通过函数调用使用 useState 挂钩设置状态
【发布时间】:2020-11-13 21:24:53
【问题描述】:

我已经像这样设置了我的初始状态对象:

const [Item,SetItem] = useState(
        {
            description: "Chicken",
            foodNutrients:[
            {nutrientName: "Protein", nutrientNumber: "203", value: 10},
            {nutrientName: "Fat", nutrientNumber: "204",  value: 15},
            {nutrientName: "Carbs", nutrientNumber: "205", value: 20}
            ]
        }
    )

我想通过循环遍历 foodNutrient 数组并将按钮单击时的值加倍来修改现有对象,然后将更新后的对象存储在 useState 挂钩中。

 const Double = e => {
        SetItem(Item.foodNutrients.forEach(foodNutrient => foodNutrient.value *= 2))
        console.log(Item)
    }

当我第一次尝试单击<button onClick={Double}>Set state</button> 时,它会将正确的结果记录到控制台,但第二次它会抛出Cannot read property 'foodNutrients' of undefined,我无法渲染任何内容。

【问题讨论】:

  • React 状态通常被认为是不可变的,在 useState 钩子的情况下它应该是不可变的。这意味着当状态改变时应该有一个新对象

标签: javascript arrays reactjs react-hooks


【解决方案1】:

事件处理程序中的逻辑是用undefined 替换初始对象,因为Array.prototype.forEach 默认返回undefined

你需要做的是:

  1. map 到具有新值的数组
  2. 保留之前的状态并将状态中的数组值设置为新的映射数组
const Double = (e) => {
  const foodNutrientsNew = Item.foodNutrients.map((foodNutrient) => ({
    ...foodNutrient,
    value: foodNutrient.value * 2,
  }));
  SetItem((prev) => ({ ...prev, foodNutrients: foodNutrientsNew }));
};

更新

我还建议重构您的代码并将其拆分为更多组件。只需关注官方文档文章here

【讨论】:

  • @EstusFlask 同意。固定
【解决方案2】:

更好的方法是在 onClick 处理程序中执行此操作 -

 const Double = e => {

    // clone your Item object
    const cloneItem = {...Item};

    // now make modifications in the new object
    cloneItem.foodNutrients.forEach(foodNutrient => foodNutrient.value *=2 );      
     
    SetItem(cloneItem);

    // to see the difference in console
    console.log(cloneItem)
    console.log(Item)
}

【讨论】:

    【解决方案3】:

    我尝试了很多方法来帮助你,我想出了这个对我有用的解决方案。

    function App() {
      const [item, setItem] = useState({
        description: "Chicken",
        foodNutrients: [
          { nutrientName: "Protein", nutrientNumber: "203", value: 10 },
          { nutrientName: "Fat", nutrientNumber: "204", value: 15 },
          { nutrientName: "Carbs", nutrientNumber: "205", value: 20 },
        ],
      });
    
      console.log(item);
    
      const double = (e) => {
        setItem({...item}, item.foodNutrients.map(e => e.value *=2));
      }
      return <button onClick={double}>Set state</button>;
    }
    

    【讨论】: