【问题标题】:Adding new property recursively to an object fails以递归方式向对象添加新属性失败
【发布时间】:2019-08-31 10:30:25
【问题描述】:

关注Unexpected outcome when modifying an object in a function
我了解到我必须先克隆item passed to the function,然后再更改它并返回它,它适用于上述示例,但是当我在我的递归代码代码中尝试它时,它不起作用,这是一个示例显示这个:

如您所见,我打算更新属性 B 如果它存在,如果它不存在,我想创建一个属性 B 然后给它最后一个值,但由于某种原因这失败了!当然如果我事先创建了属性 B(在递归调用它之前),我可以给它赋值,但我不知道为什么需要这样做或者为什么我当前的代码不起作用!

function addB(item) {
  let newItem = { ...item };

  if (newItem.B) {
    newItem.B.value = "I am B";
  } else {
    newItem.B = {
      value: "I am B"
    };
  }

  if (newItem.children) {
    newItem.children.forEach(child => {
      //if you uncomment the code below, the code works!
      //child.B = {};
      child = addB(child);
    });
  }

  return newItem;
}

function App() {
  let parent = {
    id: 0,
    children: [
      {
        id: 1,
        children: [
          {
            id: 3
          },
          {
            id: 4
          }
        ]
      },
      {
        id: 2
      }
    ]
  };

  parent = addB(parent);

  console.log(parent);
}  

当前输出:

预期输出:

You can see this example and its result in this CodeSandBox

【问题讨论】:

    标签: javascript reactjs


    【解决方案1】:

    我认为你可以做到以下几点:

    function addB(item) {
      const newItem = { ...item, B: { value: 'I am B' } };
    
      if (newItem.children) {
        newItem.children = newItem.children.map(addB);
      }
      return newItem;
    }
    

    如果你想复制 B 属性(如果它存在)并且只设置 B.value 那么你可以这样做:

    const newItem = { ...item, B: { ...item.B, value: 'I am B' } };
    

    function addB(item) {
      const newItem = { ...item, B: { ...item.B,value: 'I am B' } };
    
      if (newItem.children) {
        newItem.children = newItem.children.map(addB);
      }
      return newItem;
    }
    
    console.log(
      addB({
        children: [
          {},
          { children: [{}, { B: { other: 2 } }] },
          { B: { something: 1 } },
        ],
      })
    );

    另一种写法是:

    var addB = item => ({
      ...item,
      B: { ...item.B, value: 'I am B' },
      ...(item.children
        ? { children: item.children.map(addB) }
        : undefined),
    });
    

    【讨论】:

    • 哦,很酷的选择,我没想到,顺便问一下,为什么我当前的代码不起作用?,错误的部分在哪里?我来自其他语言,这对我来说真的没有意义,为什么当前的代码不起作用,谢谢。
    • @AliAhmadi 您没有将newItem.children 分配给新值,而是在进行浅拷贝之后才对项目进行变异,这样它就不会影响原始项目。
    • 我不做child = addB(child); 吗?不应该制作一个浅拷贝并返回孩子,更新那个孩子吗?我希望孩子中的项目意味着 child items 得到更新。
    • @AliAhmadi 对于子项中的每个项目,您添加 B 但添加 B 不会将 B 属性添加到传入的项目,而是添加到该项目的副本(这对反应状态有好处,因为您不应该变异)。因此,如果您希望 newItem.children 更改,则需要重新分配 newItem.children 具有新值。您可以通过使用 addB 函数映射子数组来做到这一点。
    • @AliAhmadi forEach 的典型用途是用于副作用,因此您不应使用它通过生成新状态来更改状态。在您的示例中,您通过设置 item.gold 来改变项目。函数 addB 不会发生变异,因为在设置传递给 addB 的项目的值之前,它会生成一个 shallow copy 并变异该副本。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-01-11
    • 1970-01-01
    • 2018-12-06
    • 2019-05-12
    • 2016-04-05
    • 2018-03-27
    • 2017-02-10
    相关资源
    最近更新 更多