React Redux TodoList报错:多元素时点击保存触发TypeError
React Redux TodoList编辑功能报错排查
问题描述
使用React Redux开发待办事项列表时,当列表中待办项数量超过1个时,点击「保存修改」按钮会触发错误:TypeError: Cannot read properties of undefined (reading 'id'),且该错误仅在待办项数量大于1时出现。
相关代码
App.js
import React from "react"; import "./App.css"; import "bootstrap/dist/css/bootstrap.min.css"; import AddTodo from "./components/AddTodo"; import TodoItem from "./components/TodoItem"; import { useSelector } from "react-redux"; function App() { const todos = useSelector((state) => state.todos); return ( <div className="App"> <AddTodo /> <div className="todoList"> {todos && todos.map((todo) => ( <TodoItem key={todo.id} todo={todo} /> ))} </div> </div> ); } export default App;
Reducer(slice.js)
import { createSlice } from "@reduxjs/toolkit"; const todoSlice = createSlice({ name: "todos", initialState: [ { id: 1, item: "Example1", completed: false, }, { id: 2, item: "Example2", completed: false, }, ], reducers: { addTodo: (state, action) => { state.push(action.payload); }, editTodo: (state, action) => { return state.map((todo) => { if (todo.id === action.payload.id) { return { ...todo, item: action.payload.item }; } }); }, deleteTodo: (state, action) => { return state.filter((todo) => todo.id !== action.payload); }, completeTodo: (state, action) => { return state.map((todo) => { if (todo.id === action.payload) { return { ...todo, completed: !todo.completed }; } return todo; }); }, }, }); export const { addTodo, selectTodo, editTodo, deleteTodo, completeTodo } = todoSlice.actions; export default todoSlice.reducer;
TodoItem.js
import React, { useState } from "react"; import { useDispatch } from "react-redux"; import { editTodo, deleteTodo, completeTodo, selectTodo } from "../store/slice"; import Button from "react-bootstrap/Button"; import Modal from "react-bootstrap/Modal"; import Form from "react-bootstrap/Form"; export default function TodoItem({ todo }) { const dispatch = useDispatch(); const [showEdit, setShowEdit] = useState(false); const [editText, setEditText] = useState(todo.text); function handleDeleteTodo() { dispatch(deleteTodo(todo.id)); }; function handleCompleteTodo() { dispatch(completeTodo(todo.id)); }; function handleEditSave(){ dispatch(editTodo({ id: todo.id, item: editText, })); console.log(editText); setShowEdit(false); } return ( <div> <h4 style={{ textDecoration: todo.completed ? "line-through" : "none", color: todo.completed ? "grey" : "black", }} > {todo?.item} </h4> <Button variant="success" onClick={handleDeleteTodo}> Delete </Button> <Button variant="success" onClick={handleCompleteTodo}> {todo?.completed ? "Incomplete" : "Complete"} </Button> <Button variant="primary" onClick={() => { setShowEdit(true); }} > Edit </Button> <Modal show={showEdit} onHide={() => setShowEdit(false)}> <Modal.Header closeButton> <Modal.Title>Edit</Modal.Title> </Modal.Header> <Modal.Body> <Form.Label> Edit To-do </Form.Label> <Form.Control type="text" value={editText ?? ''} placeholder={todo.item} onChange={(e) => setEditText(e.target.value)} /> </Modal.Body> <Modal.Footer> <Button variant="primary" onClick={handleEditSave} > Save Changes </Button> <Button variant="secondary" onClick={() => setShowEdit(false)}> Close </Button> </Modal.Footer> </Modal> </div> ); }
AddTodo.js
import React, { useState } from "react"; import { useSelector, useDispatch } from "react-redux"; import { addTodo } from "../store/slice"; export default function AddTodo() { const [text, setText] = useState(""); const todos = useSelector((state) => state.todos); const dispatch = useDispatch(); function handleAddTodo(todo) { if (text === "") { alert("Input is Empty"); } else { dispatch( addTodo({ id: Date.now(), item: todo, completed: false, }) ); console.log({ todos }); setText(""); } } return ( <div> <input type="text" value={text} onChange={(e) => setText(e.target.value)} /> <button onClick={() => handleAddTodo(text)}>Add Todo</button> </div> ); }
错误原因及修复方案
1. Reducer中editTodo逻辑错误
在slice.js的editTodo reducer里,使用map循环时,仅当todo.id匹配时返回了新对象,不匹配的项没有返回值,导致map后的数组中出现undefined元素。当列表有多个项时,后续渲染TodoItem组件时,会尝试访问undefined的id属性,触发TypeError。
修复后的editTodo代码:
editTodo: (state, action) => { return state.map((todo) => { if (todo.id === action.payload.id) { return { ...todo, item: action.payload.item }; } // 必须返回原todo,避免出现undefined return todo; }); },
2. TodoItem组件初始状态赋值错误
在TodoItem.js中,useState(todo.text)里的todo.text是错误的,因为你的todo对象中存储内容的属性是item不是text,导致初始editText为undefined。
修复后的初始状态代码:
const [editText, setEditText] = useState(todo.item);
总结
修复以上两处问题后,点击「保存修改」按钮就不会再触发错误,多待办项的编辑功能可以正常工作。
内容的提问来源于stack exchange,提问作者gjung
相关产品推荐
相关产品推荐

