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' />
<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主页中的样本中模仿的待办事项列表样本中,我添加了一个事件,该事件在单击时会删除目标待办事项。问题是,无论我单击哪个待办事项,它都不会删除目标项目本身,而是会删除除最后一个项目以外的所有项目。为什么会发生这种错误?
最佳答案
您应该进行一些更改以使其正常工作:
使用filter
而不是splice
,因为filter
返回新数组,而不是修改现有数组。最佳做法是避免修改现有状态。
可以使用将当前状态作为第一个参数传递给todos
中函数的setState
变体,而不是在setState
之前抢先setState
。
在li
函数中可以引用的removeTodo
项目中添加唯一标识符。现在,todos.splice(todos.indexOf(et), 1);
将不会返回任何有意义的东西,因为et
是Element
,并且todos
不是Element
的数组-它是具有两个键的Object数组:和id
。您可以做的是在inputValue
中添加与data-key
属性相等的li
属性,然后在key
中引用data-key
。
放在一起时,这就是您对removeTodo
类返回的JSX所做的更改:
items.map(item => (
<li key={item.id} data-key={item.id} onClick={this.props.itemClickEvent}>{item.inputValue}</li>
))
请注意,我们将
TodoList
设置为等于data-key
。我们修改后的
item.id
将如下所示: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。
编辑:
尽管上面的解决方案很好用,但是还有一个替代解决方案,它更短,并且避免了向
removeTodo
添加data-key
属性:通过进行以下更改将
input
直接传递给item.id
:items.map(item => (
<li key={item.id} onClick={() => this.props.itemClickEvent(item.id)}>{item.inputValue}</li>
))
然后,使用其
removeTodo
参数简化removeTodo
的工作:removeTodo(id) {
this.setState(state => ({
todos: state.todos.filter(item => item.id != id)
}))
}
JSFiddle here使用新解决方案。