onClick事件只留下DOM数组的最后一项而不是删除目标

时间:2017-11-26 05:29:05

标签: javascript arrays reactjs target

class ToDo extends React.Component {
  constructor() {
    super()
    this.state = {
      todos: [],
      inputValue: null
    }
    this.changeValue = this.changeValue.bind(this)
    this.addTodo = this.addTodo.bind(this)
    this.removeTodo = this.removeTodo.bind(this)
  }

  changeValue(e) {
    this.setState({inputValue: e.target.value})
  }

  addTodo(e) {
    e.preventDefault()

    const newTodoItem = {
      id: Date.now(),
      inputValue: this.state.inputValue
    }

    if(!this.state.inputValue.length) return

    this.setState(state => ({
      todos: state.todos.concat(newTodoItem),
      inputValue: ''
    }))
  }

  removeTodo(e) {
    const et = e.target
    const todos = this.state.todos

    this.setState({
      todos: todos.splice(todos.indexOf(et), 1)
    })
  }

  render() {
    return(
      <div>
        <form onSubmit={this.addTodo}>
          <input
            type='text'
            name='todo'
            placeholder='Enter a ToDo'
            value={this.state.inputValue}
            onChange={this.changeValue}
          />
          <input type='submit' value='Add' />
          &nbsp;&nbsp;&nbsp;
          <span>ToDos: {this.state.todos.length}</span>
        </form>
        <TodoList items={this.state.todos} itemClickEvent={this.removeTodo} />
      </div>
    )
  }
}

class TodoList extends React.Component {
  render() {
    const items = this.props.items
    return(
      <ul>
        {
          items.map(item => (
            <li key={item.id} onClick={this.props.itemClickEvent}>{item.inputValue}</li>
          ))
        }
      </ul>
    )
  }
}

ReactDOM.render(<ToDo />, document.querySelector('#app'))

在我刚刚从reactjs.org主页中的示例中模仿的这个待办事项列表示例中,我添加了一个事件,用于在单击时删除目标待办事项。问题是,无论我点击哪个待办事项,而不是删除目标项本身,它都会删除除最后一项之外的所有项目。为什么会发生这样的错误?

1 个答案:

答案 0 :(得分:1)

您应该进行一些更改才能使其正常工作:

  • 使用filter代替splice,因为filter会返回数组,而不是修改现有数组。避免修改现有状态是最佳做法。
  • 不要在todos之前抓取setState,而是使用setState的变体将当前状态作为第一个参数传递给setState中的函数。
  • 为您可以在li功能中引用的removeTodo项添加唯一标识符。目前,todos.splice(todos.indexOf(et), 1);不会返回任何有意义的内容,因为etElementtodos不是Element的数组 - 它是一个具有两个键的对象数组:idinputValue。您可以执行的操作是为data-key添加li属性,其等于key属性,然后在data-key中引用removeTodo

当你把它们放在一起时,这是你对TodoList类返回的JSX所做的更改:

items.map(item => (
    <li key={item.id} data-key={item.id} onClick={this.props.itemClickEvent}>{item.inputValue}</li>
))

请注意,我们将data-key设置为item.id

我们修改后的removeTodo将如下所示:

removeTodo(e) {
    const et = e.target
    const attr = et.getAttribute("data-key");

    this.setState(state => ({
      todos: state.todos.filter(item => item.id != attr)
    }))
}

我用一个有效的例子修改了你的JSFiddle here

修改

虽然上面的解决方案运行得很好,但还有一个更短的替代解决方案,可以避免向data-key添加input属性:

通过进行此项更改,将item.id直接传递给removeTodo

items.map(item => (
    <li key={item.id} onClick={() => this.props.itemClickEvent(item.id)}>{item.inputValue}</li>
))

然后,使用其removeTodo参数简化您的id

removeTodo(id) {
    this.setState(state => ({
      todos: state.todos.filter(item => item.id != id)
    }))
 }
使用新解决方案

JSFiddle here