首页 > 解决方案 > 在 React 中推送到数组,意外行为

问题描述

尝试制作待办事项列表应用程序

我希望我的待办事项具有唯一的键/ID。我想通过获取最后一个待办事项并将 1 添加到其 id 来创建待办事项 id。如果该项目不存在,它会创建一个 id = 1 的新项目。

应用程序.js:

import React, {Component} from 'react';
import Todos from './Todos'
import AddTodo from './AddForm'

class App extends Component {

  state = {
    todos: [
      {id: 1, content: 'buy milk'},
      {id: 2, content: 'play nintendo'}
    ]
  }

 addTodo = (todo) => {
    if (this.state.todos[0])
    {
      todo.id = this.state.todos[this.state.todos.length-1].id +1;
      let todos = [...this.state.todos];
      todos.push(todo);
      this.setState({
        todos: todos
      });
    }
    else
    {
      this.setState({
        todos: [
          {id: 1, content: todo.content}
        ]
      });
    }
    console.log(this.state.todos);
  }


  render() {
    return(
      <div className="todo-app container">
        <h1 className="center blue-text">Todos:</h1>
        <Todos deleteTodo={this.deleteTodo} todos={this.state.todos}/>
        <AddTodo addTodo={this.addTodo} />
      </div>
    );
  }
}

这几乎可以工作。如果项目具有独特的内容,我可以添加新项目,但是当项目具有相同的内容时会出现问题。

独特的内容console.log:

0: {content: "buy milk", id: 1}
1: {content: "play nintendo", id: 2}
2: {content: "adrfhahhafafdhafhafhhfa", id: 3}
3: {content: "adrfhahhafafdhafhafhhfaafhafhfah", id: 4}
4: {content: "adrfhahhafafdhafhafhhfaafhafhfahafhfahafh", id: 5}
5: {content: "adrfhahhafafdhafhafhhfaafhafhfahafhfahafhafhafhfah", id: 6}

相同的内容console.log:

0: {content: "buy milk", id: 1}
1: {content: "play nintendo", id: 2}
2: {content: "adrfhahhafafdhafhafhhfa", id: 3}
3: {content: "adrfhahhafafdhafhafhhfaafhafhfah", id: 4}
4: {content: "adrfhahhafafdhafhafhhfaafhafhfahafhfahafh", id: 5}
5: {content: "adrfhahhafafdhafhafhhfaafhafhfahafhfahafhafhafhfah", id: 7}
6: {content: "adrfhahhafafdhafhafhhfaafhafhfahafhfahafhafhafhfah", id: 7}

如您所见,如果内容相同,则 id 相同。

我知道这可能是在反应中添加 id:s 的错误方法,但我想了解发生了什么。提前致谢。

编辑:如果项目被删除,Yevgen 指出我的逻辑存在缺陷,但我仍然不明白为什么ID:s 重复。有人要求提供其余代码,所以我将其全部添加。

AddForm.js

import React, {Component} from 'react';

class AddTodo extends Component {
  state = {
    content: ''
  }

  handleChange = (e) => {
    this.setState({
      content: e.target.value
    });
  }

  handleSubmit = (e) => {
    e.preventDefault();
    this.props.addTodo(this.state);
  }

  render(){
    return (
    <div>
      <form onSubmit={this.handleSubmit}>
        <label>Add new todo</label>
        <input type="text" onChange={this.handleChange} />
      </form>
    </div>
    );
  }
}

export default AddTodo;

Todos.js

import React from 'react'

const Todos = ({todos, deleteTodo}) => {
  const todoList = todos.length ? (
    todos.map(todo => {
      return (
        <div className="collection-item" key={todo.id}>
          <span onClick={() => {deleteTodo(todo.id)}}>{todo.content}</span>
        </div>
      )
    })
  ) : (
    <p className="center">You have no todos left</p>
  )
  return (
    <div className="todos collection">
      {todoList}
    </div>
  )
}

export default Todos;

标签: javascriptarraysreactjs

解决方案


我的猜测是您的问题来自您声明<AddForm />组件状态的方式。您应该在 constrictor 体内完成此操作,如this.state. 您当前所做的是全局变量声明。因此,它不受您的应用程序状态的约束,并且您id的分配不正确。

除此之外,您的addTodo()外观过于复杂且容易出错(您不能依靠数组长度来获得最大使用id量,因为删除项目,而不是最后一个会破坏逻辑)。

此外,通常建议避免在类方法中使用箭头符号。相反,您应该将它们声明为常规函数并做一些事情,例如this.addTodo = this.addTodo.bind(this)在您声明的每个方法的构造函数主体中。否则,如果您非常喜欢箭头函数,那么您可能会更喜欢函数组件。

按照您的逻辑分配 autoincremented id(因此,id已删除项目的 's 永远不会被重用)并将具有属性的对象传递contentaddTodo(),您可以简单地完成:

addTodo({content}){
   const id = Math.max(...this.state.todos.map(({id}) => id),0)+1
   this.setState({todos:[...this.state.todos, {id, content}]})
}

推荐阅读