我正在React中创建一个简单的待办事项应用程序。最后,我在修修补补,想用各种方法取得同样的结果。
当我在render
中用map
方法填充数组时,一切都很好:
const todosItems = this.state.todos.map((todo) =>
<TodoItem name={todo.text} key={todo.id} onClick={(e) => this.deleteButtonClick(todo.id)} />
);
当我用for
循环做同样的事情时:
const todosItems = [];
const todos = this.state.todos;
for (let i = 0; i < todos.length; i++) {
todosItems.push(<TodoItem name={todos[i].text} key={todos[i].id} onClick={(e) => this.deleteButtonClick(todos[i].id)} />);
}
当我删除其中一个项目时应用程序崩溃:
[Error] TypeError: undefined is not an object (evaluating 'todos[i].text')
_loop (index.js:17650)
render (index.js:17659)
finishClassComponent (index.js:5285:149)
performUnitOfWork (index.js:5931:360)
workLoop (index.js:5938)
callCallback (index.js:2613:108)
dispatchEvent
invokeGuardedCallbackDev (index.js:2633)
invokeGuardedCallback (index.js:2649:791)
replayUnitOfWork (index.js:5800:88)
renderRoot (index.js:5960:160)
performWorkOnRoot (index.js:6151)
performWork (index.js:6133:813)
performSyncWork (index.js:6131:155)
interactiveUpdates$1 (index.js:6184:488)
dispatchInteractiveEvent (index.js:3758:115)
dispatchInteractiveEvent
完整代码:https://github.com/ArturKot95/todo-react/blob/master/index.js
提前感谢您的帮助。
编辑:感谢您的解决方案:D。在学习React时,对我来说最重要的是认为状态就像程序中的快照,不能直接影响组件的状态,而是在副本上工作。
将delete
替换为Array.prototype.filter
:
deleteButtonClick = (id) => {
let todos = this.state.todos.slice();
todos = todos.filter(todo => todo.id !== id);
this.setState({
todos: todos
});
}
用const todos = this.state.todos.slice()
代替const todos = this.state.todos
delete
运算符导致错误
使用delete运算符删除数组元素时,数组长度不受影响。
删除数组元素
这意味着如果数组有三个todo[{todo}, {todo}, {todo}]
删除第二个元素时:delete todos[1]
todo将更改为:[{todo}, empty, {todo}]
正如您所看到的,数组的长度仍然是三,因此错误发生在for语句中。
map
工作原理:
映射回调仅针对已分配值(包括未定义值(的数组索引调用。它不会为数组中缺少的元素(即从未设置、已删除或从未分配值的索引(调用。
地图描述
PS:当你检查错误时,你可以添加一些日志来检查发生的时间。:D
delete
操作在数组中留下一个空对象,当循环到达它时,它无法获得其id
属性。一般来说,代码可以通过多种方式进行改进:
delete
在数组上不是可行的方法。在删除中使用筛选器:this.setState({this.state.todos.filter(todo => todo.id !== id)})
- 不鼓励使用推送,只需映射/过滤您的数组即可
要解决此错误,您需要将delete方法中的代码更改为以下代码:
for(var i=0; i<todos.length; i++) {
if (todos[i].id === id) {
todos.splice(i, 1)
}
}
现在您正在直接改变代码块中的状态:
const todos = this.state.todos;
for (let todo in todos) {
if (todos[todo].id === id) {
delete todos[todo];
}
}
这可能会导致应用程序中出现各种问题。正如Meir所提到的,您通常想要映射/过滤您的数组,但我想我也要注意,将todos状态的值分配给一个新对象,修改它,然后用这个新对象设置状态,将与您当前设置的方式一致。
const todos = Object.assign({}, this.state.todos);
不可变数据是react应用程序的推荐方式。不要删除数组中的元素。创建一个没有所需元素的新元素。最快的方法是将元素推送到一个新的数组中。
deleteButtonClick = (id) => {
const todos = this.state.todos;
let res = [];
for (let i = 0; i < todos.length; i++) {
if (todos[i].id !== id) {
res.push(todos[i]);
}
}
this.setState({
todos: res
});
}
由于它是一个新数组,所以不会出现delete运算符导致长度错误的问题。