使用钩子从数组中删除对象(useState)

Posted

技术标签:

【中文标题】使用钩子从数组中删除对象(useState)【英文标题】:Removing object from array using hooks (useState) 【发布时间】:2019-12-11 23:17:47 【问题描述】:

我有一个对象数组。我需要添加一个函数来从我的数组中删除一个对象,而不使用“this”关键字。

我尝试使用updateList(list.slice(list.indexOf(e.target.name, 1)))。这会删除数组中除最后一项之外的所有内容,我不确定为什么。

const defaultList = [
 name: "ItemOne" ,
 name: "ItemTwo" ,
 name: "ItemThree" ]

const [list, updateList] = useState(defaultList);

const handleRemoveItem = e => 
    updateList(list.slice(list.indexOf(e.target.name, 1)))


return (
    list.map(item => 
        return ( 
            <>
            <span onClick=handleRemoveItem>x </span>
            <span>item.name</span>
            </>
        )
    

)

预计:点击的项目将从列表中删除。 实际:整个列表被删除,减去数组中的最后一项。

提前感谢您的任何意见!

【问题讨论】:

对于初学者来说,跨度上没有name。做一些基本的调试,看看e.target.name 是什么。然后...阅读文档以了解 slice() 的工作原理以及 indexOf() 的工作原理。您在该方法中有多个问题 你说得对,跨度上没有名字。这就是为什么名称指的是从数组映射的项目。你甚至读过这篇文章吗? 不,它不在您使用onClick=handleRemoveItem 的上下文中。你在函数内部检查了吗? @collinswade408 请参阅下面的解决方案。这应该让您了解如何完成您的解决方案以及您的代码为什么不起作用。 【参考方案1】:

首先,带有点击事件的span 元素需要有一个name 属性,否则在e.target 中将找不到名称。话虽如此,e.target.name 保留用于表单元素(输入、选择等)。因此,要真正利用 name 属性,您必须使用 e.target.getAttribute("name")

另外,因为你有一个对象数组,所以使用list.indexOf(e.target.name) 是无效的,因为当你迭代对象时它正在寻找string。这就像在[, , ] 中找到“狗”

最后,array.slice() 返回一个新数组从您传递给它的索引处的项目开始。因此,如果您单击最后一项,则只会返回最后一项。

改用.filter():codesandbox

import React,  useState  from "react";
import ReactDOM from "react-dom";

import "./styles.css";

const App = () => 
  const defaultList = [
     name: "ItemOne" ,
     name: "ItemTwo" ,
     name: "ItemThree" 
  ];

  const [list, updateList] = useState(defaultList);

  const handleRemoveItem = (e) => 
   const name = e.target.getAttribute("name")
    updateList(list.filter(item => item.name !== name));
  ;

  return (
    <div>
      list.map(item => 
        return (
          <>
            <span name=item.name onClick=handleRemoveItem>
              x
            </span>
            <span>item.name</span>
          </>
        );
      )
    </div>
  );
;

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

【讨论】:

&lt;span&gt; 不能有 name 属性或不正确。 @WillJenkins 当然可以,只是您无法通过event.target.name 看到它。 非常感谢!我将其更改为不是跨度,但这对我有用。 @collinswade408 欢迎您的朋友,我希望这对您有帮助并祝您编码愉快! 检查规范,&lt;span&gt; 不能有name w3.org/TR/REC-html40/index/attributes.html【参考方案2】:

您可以使用Array.filter 在单行中执行此操作:

const handleRemoveItem = name => 
    updateList(list.filter(item => item.name !== name))

Eta:您还需要在 onClick 处理程序中传递您的项目名称:

list.map(item => 
    return ( 
        <>
        <span onClick=() =>handleRemoveItem(item.name)>x </span>
        <span>item.name</span>
        </>
    )

【讨论】:

【参考方案3】:

我遇到了同样的问题,并使用此问题的答案提出了解决方案。我将我的最终解决方案放在这里,因为我必须使用多个答案才能到达那里。

const defaultList = [
     name: "ItemOne" ,
     name: "ItemTwo" ,
     name: "ItemThree" 
]

const [list, updateList] = useState(defaultList);

const handleRemoveItem = idx => 
    // assigning the list to temp variable
    const temp = [...list];

    // removing the element using splice
    temp.splice(idx, 1);

    // updating the list
    updateList(temp);


return (
    list.map((item, idx) => (
      <div key=idx>
        <button onClick=() => handleRemoveItem(idx)>x </button>
        <span>item.name</span>
      </div>
    ))

)

【讨论】:

这个例子完成后临时变量不会留在内存中吗?这对大工作量来说是不利的。 @IanSmith 对此有何建议?【参考方案4】:

我认为这段代码会做

let targetIndex = list.findIndex((each) => each.name == e.target.name);
list.splice(targetIndex-1, 1);

我们需要检查对象内部的名称值,因此请使用 findIndex。然后将对象从目标索引开始切割到目标索引之后的1个数组。

Codepen

根据您的评论,您的问题来自另一部分。

更改此视图部分

    return ( 
        <>
        <span onClick=() => handleRemoveItem(item) >x </span>
        <span>item.name</span>
        </>
    )

改变函数handleRemoveItem格式

const handleRemoveItem = item => 
    list.splice(list.indexOf(item)-1, 1)
    updateList(list);

【讨论】:

最好不要改变原始数组 感谢您的快速回答,但这会返回打字稿错误Argument of type '(each: name: string; ) =&gt; void' is not assignable to parameter of type '(value: name: string; , index: number, obj: name: string;[]) =&gt; boolean'. 您可以查看代码笔。我认为问题在另一部分而不是这部分; 我不知道你为什么使用 const,因为它不可编辑。 完成。你可以再检查一遍。希望你的问题消失了!【参考方案5】:

我认为迄今为止的最佳答案的小改进

import React,  useState  from "react";
import ReactDOM from "react-dom";

import "./styles.css";

const App = () => 
  const defaultList = [
     name: "ItemOne" ,
     name: "ItemTwo" ,
     name: "ItemThree" 
  ];

  const [list, updateList] = useState(defaultList);

  const handleRemoveItem = (item) => 
    updateList(list.filter(item => item.name !== name));
  ;

  return (
    <div>
      list.map(item => 
        return (
          <>
            <span onClick=()=>handleRemoveItem(item)>
              x
            </span>
            <span>item.name</span>
          </>
        );
      )
    </div>
  );
;

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

我们只是将其发送到句柄函数,而不是提供名称属性

【讨论】:

我认为有 2 个小错误。将它们修复为: 1) const handleRemoveItem = (name) => ... // 等 2) onClick=()=>handleRemoveItem(item.name)【参考方案6】:

这是因为slice 和splice 都返回一个包含已删除元素的数组。

你需要给数组应用一个splice,然后然后使用钩子提供的方法更新状态

const handleRemoveItem = e => 
    const newArr = [...list];
    newArr.splice(newArr.findIndex(item => item.name === e.target.name), 1)
    updateList(newArr)

【讨论】:

感谢您的快速回复。不幸的是,上述方法不起作用,所以我尝试了const newArr= list.splice(list.indexOf(e.target.name, 1)) updateList(newArr),这给了我与原始帖子相同的问题。 :( 那是因为 splice 方法仍然返回一个被移除元素的数组,即使你将它存储在一个变量中。 你不能以这种方式在对象数组上使用indexOf() 是的,我只是做了一个编辑,我从问题中复制/粘贴了这部分代码,并没有注意数据结构。

以上是关于使用钩子从数组中删除对象(useState)的主要内容,如果未能解决你的问题,请参考以下文章

React Native:如何从循环中获取值到钩子 useState

useState 钩子一次只能设置一个对象,并将同一个数组的另一个对象返回到初始状态

在 useState 钩子中反应设置状态

如何使用我的反应应用程序中的 useState 钩子给出的 setState 方法增加状态对象的选定索引的计数

反应钩子:useState/context;无法读取未定义的属性“头像”/如何更新嵌套对象

在对数组进行排序并传递它之后,React 钩子 useState/setState 不起作用