如何避免向数组中添加已存在的元素

3
我正在使用React实现添加到收藏夹的功能。在大家的帮助下,我已经成功实现了这个功能,但是无法切换喜欢和不喜欢。我编写的代码是"likes: this.state.likes.filter(e => e.name !== person.name)",因为有人建议我这样编码。说实话,我不理解上面的代码,可能是因为它是ES6语法。在ES5中,它是什么样子的呢?而且现在这段代码不起作用,元素没有正确地添加到数组中。我该如何修复这段代码呢?最初的回答:我建议你先了解ES6语法中的箭头函数和filter方法。箭头函数用于简化函数的书写方式,而filter方法可以根据指定的条件过滤数组中的元素。在ES5中,可以使用普通的函数和for循环来实现相同的功能。关于代码不起作用的问题,你需要检查一下传入filter方法的参数是否正确,以及是否正确地更新了state中的likes数组。
import React from 'react';
import axios from 'axios';
import IcoMoon from 'react-icomoon';

export default class PersonList extends React.Component {

    state = {
        persons: [],
        likes: []
    }

    componentDidMount() {
        axios.get(`https://jsonplaceholder.typicode.com/users`)
            .then(res => {
                const persons = res.data;
                this.setState({ persons });
            })
    }

    handleClick = person => {

        if (this.state.likes.filter(e => e.name === person.name).length > 0) {

            this.setState({
                likes: this.state.likes.filter(e => e.name !== person.name)
            });

            console.log(this.state.likes);
            return;
        }

        this.setState({
            likes: [...this.state.likes, person]
        });       
    };

    likesTemplate = item => <li key={item}>{item}</li>;

    renderLikes = () => {
        return this.state.likes.map(i => this.likesTemplate(i));
    }

    render() {
        return (
            <div>
                {this.state.persons.map(person => {
                return <li key={person.name}><IcoMoon icon="heart" onClick={() => {this.handleClick(person.name)}} />{person.name}</li>}
            )}

            <h2>Favorite Person</h2>
            <ul>
                {this.renderLikes()}
            </ul>
            </div>
        )
    }
}

1
你尝试过使用Array.prototype.includes()来检查数组中是否存在某个元素吗?在这里可以查看详细信息:https://developer.mozilla.org/it/docs/Web/JavaScript/Reference/Global_Objects/Array/includes - Joe Taras
1
使用Set而不是数组可能也是个好主意。毕竟避免重复是Set的本质。https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Set - buffy
我试过了,它有效!"if (this.state.likes.includes(person)) {"。现在我的问题是我无法弄清楚如何从数组中删除重复的元素。我尝试像这样:"this.setState({ likes: this.state.likes.splice(person) });",但它不起作用 :( - kayak
1
尝试使用以下代码:this.state.likes.splice(this.state.likes.indexOf(person), 1) - buffy
尝试了上述代码,但似乎把一切都弄糟了。如果我第一次点击,数组仍然是空的,而如果我点击另一个人,则之前的人会被添加到数组中。 - kayak
@kayak,我刚给你写了一个解决方案。如果有帮助,请告诉我! - Chris Ngo
2个回答

6
正确的条件是:
if (!(this.state.likes.filter(e => e.name === person.name).length > 0)) {
  // your code
}

条件的含义是:
这个条件会过滤出只有当数组中的一个元素与提供的 person 对象 name 属性相匹配时才起作用。它指出该 person 已经存在于数组中。
"!" 用于检查相反的情况,因为只有在 person 不存在时才能进入 if 语句。
编辑:仔细阅读代码后,可以使用更简单、更清晰的方法实现这一点。
  addPerson = (person) => {
    let filteredPerson = this.state.likes.filter(like => like.name !== person.name);
    this.setState({
      likes: [...filteredPerson, person]
    })        
  }

查看这个可工作的代码片段: https://stackblitz.com/edit/react-jxebwg


1
修复了一个括号问题。你也可以选择另一种方式来检查长度是否小于1,不需要使用下划线! - Mosè Raguzzini
所以如果数组中不包含person,那么就splice?也许是相反的吗? - kayak
如果我点击一个人然后再次点击,它肯定会切换状态,但是如果在切换之前我点击另一个人,喜欢的数组就会变为空。顺便说一下,谢谢你提供的链接。那个服务看起来很有用。 - kayak

0
你可以用几种不同的方法来解决这个问题。

编辑:现在我们知道likes只是一个字符串数组

使用.includes()

handleClick = person => {
    const likes = this.state.likes
    //check to see if person has already liked 
    if (likes.includes(person)) {
       //user has already liked. Choose one option (1 or 2)
       
       //Option 1: create a new like array without the current user
       const newLikes = likes.filter((like) => like !== person)
   
       //Option 1: update state with new likes
       this.setState({
          likes: newLikes
       })

       //Option 2: or do nothing
       return;

    //this executes if the user has not liked yet
    } else {
      this.setState({
        likes: [...likes, person]
      })
    }    
};

所以我所说的“执行以下操作之一”是根据您希望功能如何运作而定。

选项1: 如果您想创建一种切换效果,即用户已经喜欢该项目。您应选择选项一,我们将创建一个新的数组并更新状态。这将“取消喜欢”,从喜欢的数组中移除用户。然后点击

选项2: 就是什么都不做。如果用户点击按钮并且他们已经喜欢了它,这将保持状态不变。


非常感谢您详细的解释。我之前不知道.filter可以移除特定元素。然而,我的代码并没有按照我预期的方式工作。即使我点击其他图标,切换仍然会发生。 - kayak
1
@kayak 我不确定你说的其他图标是什么意思?你是指当你点击一个图标时,属于不同的图标会发生变化吗? - Chris Ngo
抱歉造成困惑。也许我应该这样写:"if (likes.map(like => like).includes(person))"?我移除了所有的“.name”,最终它终于成功了! - kayak
不确定 "if (likes.map(like => like).includes(person))" 的语法是否正确.. - kayak
@kayak 啊好的,我知道出了什么问题。当你渲染这些图标时,你已经将person.name传递到了handleClick函数中。另外看起来likes数组只是一个字符串数组,其中只包含人名。我以为它是一个对象。在这种情况下,我们只需要.includes()。让我更新我的答案来清理解决方案。 - Chris Ngo
让我们在聊天中继续这个讨论。 - Chris Ngo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接