javascript - 复选框未更改 React 组件中的状态
问题描述
我正在使用 React 创建一个 todolist 应用程序。待办事项的数据如下所示:
const todoData = [
{
id: 1,
text: "Empty bin",
completed: true
},
{
id: 2,
text: "Call mom",
completed: false
}
]
现在,我有一个 App 组件,我可以在其中导入该数据并将其保存在状态中。
import todoData from "./todoData"
class App extends React.Component {
constructor() {
super()
this.state = {
todos: todoData,
}
this.handleChange = this.handleChange.bind(this)
}
...
我还有一个 handleChange 方法,它应该将已完成属性的值更改为其反值。例如:对于 id 为 1 的待办事项,其文本值为“Empty Bin”并且已完成为 true,因此默认情况下会选中复选框。然而,当它被点击时,完成应该是假的并且复选框不应该再被点击。出于某种原因,这不会发生,因此 completed 保持其默认布尔值并且不会翻转。因此,当单击复选框时,不会发生任何更改。
handleChange(id) {
this.setState(prevState => {
const updatedTodos = prevState.todos.map(todo => {
if (todo.id === id) {
todo.completed = !todo.completed
}
return todo
})
return {
todos: updatedTodos
}
})
}
使用 console.log 后,我意识到 todo.completed 确实被更改为相反的值,但由于某种原因,即使在 devtools map() 中表示该值在返回一个新数组时已更新,但由于某种原因,它在 updatedTodos 中并未更改存储在 updatedTodos 中。因此,状态不会改变,复选框不能被点击
TodoItem 功能组件与 App 组件位于一个单独的文件中,并包含 todo 元素的 HTML。如下图所示:
function TodoItem(props) {
return (
<div className="todo-item">
<input type="checkbox"
checked={props.task.completed}
onChange={() => props.handleChange(props.task.id)}/>
<p>{props.task.text}</p>
</div>
)
}
此外,TodoItem 在 App 组件中呈现
render() {
const todoArray = this.state.todos.map(task => <TodoItem key={task.id}
task={task} handleChange={this.handleChange}/>)
return (
<div className="todo-list">
{todoArray}
</div>
)
}
解决方案
handleChange
看起来你正在改变函数内部的 todo 对象
handleChange(id) {
this.setState(prevState => {
const updatedTodos = prevState.todos.map(todo => {
if (todo.id === id) {
//todo.completed = !todo.completed this is mutating
// in react mutating a object will result in unexpected results like this.
// so you have to create a new object based on the current todo and return it
return {
...todo,
completed: !todo.completed
}
}
return todo
})
return {
todos: updatedTodos
}
})
}
那里发生了什么?
数组中的每个对象都指向一个内存位置,基本上如果我们更改对象属性值(对于 EX: completed
),而不更改它正在变异的内存位置,
通过这样做,todo.completed = !todo.completed
我们直接改变了completed
属性的值,但 todo 对象仍然指向相同的内存位置,所以我们改变对象并且 react 不会响应它,现在通过这样做
return {
...todo, // <- create a new object based on todo
completed: !todo.completed // <- change the completed property
}
我们基于待办事项创建一个新对象{...todo}
(新对象 = 新内存位置),并更改completed
=>的值{...todo, completed: !todo.completed}
,因为它指向新的内存位置,react 将响应更改。
如果您不熟悉展开运算符 ( ...
),请在此处阅读,不要忘记查看此处的对象文字部分中的展开
推荐阅读
- java - java中如何从List中获取id
- image-processing - 用于比较二值图像的度量
- android - Flutter:如何使用 RaisedButton 在 PageView 中导航或使用按钮移动 pageViewController
- java - 如何在 Spring Boot 应用程序中编写静态资源文件
- python - 在 Django 管理面板中加载页面时将字段值初始化为 null
- laravel - 请求 Laravel 雄辩的结构或其他方式
- arrays - 获取 cosmos db 中数组中不包含的结果
- mybatis - ibatis地图列表
进入@Result - python - 在命令提示符下使用 pip 时出现语法错误
- python-3.x - Numpy 无法访问列