通过展开操作删除对象键 returns undefined

Remove object key by spread operated returns undefined

我正在创建单选按钮对象。数组中的每个对象都有对象 {value: 1, text: 'Sometext'} 并且如果单选按钮是 selected,添加 selected: true 到对象中并删除 select来自其他人。

const onChoiceChange = function setChoiceStateAndUpdateSelectedObject(e) {
    let updating = []
    choices.map((item, index) => {
        if (item.value == e.target.value) {
            updating.push({...item, selected: true})
        } else {

                item.selected && updating.push({selected, ...item} = item) //here it says selected undefined even with the condition check          
                !item.selected && updating.push(item)
        }
    })
    setChoices(updating)
    console.log('updating', updating)
}

我在这里使用代码片段工具,它可以工作,但是当我在 NextJS 中编译我的代码时,出现以下错误。

我在片段中使用相同的代码,我不明白为什么当我第二次 select 一个不同的单选按钮时它说 selected 未定义。

let choices = [
{ value: 1, text: 'This is value 1' },
{ value: 2, text: 'This is value 2' },
{ value: 3, text: 'This is value 3' },
{ value: 4, text: 'this is value 4'}
]

document.addEventListener('input',(e)=>{
  let updating = []
if(e.target.getAttribute('name')=="choices") {


  choices.map((item, index) => {
    if (item.value == e.target.value) {
          updating.push({...item, selected: true})
    } else {
          item.selected && updating.push({selected, ...item} = item)           
          !item.selected && updating.push(item)
    }
  })
}
console.log(updating)
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>


<input type='radio' name='choices' value='1'/>
<input type='radio' name='choices' value='2'/>
<input type='radio' name='choices' value='3'/>
<input type='radio' name='choices' value='4'/>

请将 else 块中的行更新为

                item.selected && updating.push(item) //         

好的,所以 selected 对于解析器来说是未知的 variable/entity 因为您没有在代码中的任何地方定义。 selected:item.selected 将使 selected:true.

请在下面找到工作片段:

let choices = [{
    value: 1,
    text: 'This is value 1'
  },
  {
    value: 2,
    text: 'This is value 2'
  },
  {
    value: 3,
    text: 'This is value 3'
  },
  {
    value: 4,
    text: 'this is value 4'
  }
]

document.addEventListener('input', (e) => {
  let updating = []
  if (e.target.getAttribute('name') == "choices") {
    choices.map((item, index) => {
      if (item.value == e.target.value) {
        updating.push({ ...item,
          selected: true
        })
      } else {
        item.selected && updating.push({
          selected:item.selected,
          ...item
        } = item) 
        !item.selected && updating.push(item)
      }
    })
  }
  console.log(updating)
})
<html>

<head>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
</head>

<body>
  <input type='radio' name='choices' value='1' />
  <input type='radio' name='choices' value='2' />
  <input type='radio' name='choices' value='3' />
  <input type='radio' name='choices' value='4' />
</body>

</html>

您正在混淆 rest and spread 语法。两者都是椭圆(...),但基本上是相反的。

在您的真实情况下,您正在使用传播 {...item, selected: true},将 item 中的所有值设置为新对象,然后添加键 selected(可能覆盖项目中的先前 selected 键)。工作正常。

在你的 else 情况下,你正在使用 rest(并且通过扩展,destructuring{selected, ...item} = item。解构(和 rest)仅是 asignment。解构创建新变量,或者,如果用括号括起来,则重新分配旧变量。休息,特别是意味着 "take all of the entries I didn't ask for by name and shove them into a new object called [blank]"。你不是在向数组添加新对象。你正在将 items.selected 的值分配给一个未定义的变量 selected,并试图将 item 中的其余条目重新分配给一个新对象...也称为 item。这不会起作用。永远。如果你正在尝试删除 item.selected,那么您可以像这样使用解构:

...
} else {
    let {selected, ...cleanedItem} = item;

    item.selected && updating.push(cleanedItem);
    !item.selected && updating.push(item);
}
...


在你的其他情况下,你也可以从 item delete selecteddelete 确实会改变原始对象,通常最好避免这种情况,但如果您知道它是安全的(取决于您的代码的其余部分),那么它是一个有效的选项。这是一个例子:

...
} else {
    delete item.selected;
    updating.push(item);
}
...


第三种删除selected is to do the same thing you do in your true case, but settingselectedtoundefined`的方法:

...
} else {
    updating.push({...item, selected: undefined});
}
...


但是,如果你走那条路,你也可以一起摆脱 if 并使用 ternary operator (expresion ? [true case] : [false case]) 代替。它不能在这两种情况下执行复杂的逻辑,但您不需要这样做。看起来像这样:

...
    // replacing the if/else
    updating.push({
        ...item, 
        selected: item.value == e.target.value
            ? true
            : undefined
    });
...

或者,如果 false 对 selected 有效,您可以跳过任何条件分支:

...
    // replacing the if/else
    updating.push({
        ...item, 
        selected: item.value == e.target.value
    });
...


当我们这样做时,您可能想要一起放弃对 updating 数组的推送。 Map,不像forEach,是设计成return一个数组。只是 return 您要为数组中的项目设置的值:

const onChoiceChange = function setChoiceStateAndUpdateSelectedObject(e) {
    let updating = choices.map((item, index) => {
        return {
            ...item, 
            selected: item.value == e.target.value
        }
    });
    setChoices(updating)
    console.log('updating', updating)
}


把它们放在一起(折叠,因为这个答案很长):

let choices = [
  { value: 1, text: 'This is value 1' },
  { value: 2, text: 'This is value 2' },
  { value: 3, text: 'This is value 3' },
  { value: 4, text: 'this is value 4' }
];

document.addEventListener('input', (e) => {
  let updating = [];

  if (e.target.getAttribute('name') == "choices") {
    updating = choices.map(item => ({
      ...item,
      selected: item.value == e.target.value
    }));
  }
  console.log(updating)
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

<input type='radio' name='choices' value='1' />
<input type='radio' name='choices' value='2' />
<input type='radio' name='choices' value='3' />
<input type='radio' name='choices' value='4' />



作为临别注,首先要说明语法错误的原因。上面关于解构的部分提到了它,但我想我会直接解决它。解构时,您需要使用声明性关键字 (let, const, orvar`) 来创建新变量,或者使用现有变量和括号。 您不能混合搭配,您需要使用所有新变量或重用所有现有变量。像这样:

// new variables
//  - can't be the same name as a defined variable in scope
let {selected, ...cleanedItem} = item;

// or with existing variables
//  - the existing variables must be in scope
//  - we'll assume item is in scope, same as your examples
let selected;

({selected, ...item} = item);

所以你得到语法错误的原因是因为你试图将新旧变量混合在一起。 selected(正如其他人所指出的)未定义。除此之外(虽然不会抛出任何错误)如果你尝试 return 解构的结果,你实际上 return 你正在解构的未修改的原始项目。所以 selected 根本不会被删除,即使解构语法被正确使用。好玩,不是吗?

您定义的其他情况在逻辑上不正确。映射函数 returns 一个被映射类型的对象。所以你可以直接将 return 分配给一个变量。

let updating = choices.map((item, index) => { /* logic  */}

否则,您可以简单地推送到更新数组,或者如上所述,您可以简单地 return 地图中的项目。

let updating = choices.map((item, index) => {
    if (item.value == e.target.value) {
        return {...item, selected: true};
    } else {
        return item;
     // can push as below if you are not returning from map
     // updating.push(item);
});