React+Redux Toolkit TodoList删除项后报todos.map非函数错误求助
问题修复:删除待办项后出现
TypeError: todos.map is not a function 错误根源
- 异步Thunk类型重复:
removeTodosThunk的action type写成了'todo/fetchTodos',和获取待办列表的fetchTodos完全一致。这导致删除操作完成后,会触发fetchTodos.fulfilled的reducer逻辑,把删除接口返回的单个Todo对象(而非数组)赋值给state.todos,后续map调用自然报错。 - Thunk返回类型错误:
removeTodosThunk定义的返回类型是Todo[],但DELETE接口通常返回的是被删除的单个Todo对象,类型不匹配。 - 缺少删除操作的Reducer处理:没有为
removeTodosThunk添加对应的extraReducers逻辑,反而错误复用了获取列表的逻辑。
修复步骤
1. 修正removeTodosThunk的定义
修改todoSlice.ts中的删除异步Thunk:
// 修正type名称和返回类型 export const removeTodosThunk = createAsyncThunk<Todo, number>( 'todo/removeTodo', // 改为唯一的type标识 async function(id) { const {data} = await axios.delete(`http://localhost:3004/todos/${id}`); return data; // 返回单个被删除的Todo对象 } )
2. 添加删除操作的Reducer处理
在todoSlice的extraReducers中新增removeTodosThunk的处理逻辑:
extraReducers: (builder) => { builder.addCase(fetchTodos.pending, (state) => { state.status = 'loading'; state.error = null; }) .addCase(fetchTodos.fulfilled, (state, action) => { state.status = 'resolved'; state.todos = action.payload; }) .addCase(addTodosThunk.fulfilled, (state, { payload } : { payload: Todo }) => { console.log(payload); state.todos.push(payload); }) .addCase(fetchTodos.rejected, (state, action) => { state.status = 'rejected'; }) // 新增删除完成的处理逻辑 .addCase(removeTodosThunk.fulfilled, (state, action) => { state.todos = state.todos.filter(todo => todo.id !== action.payload.id); state.status = 'resolved'; }) .addCase(removeTodosThunk.rejected, (state) => { state.status = 'rejected'; state.error = '删除失败'; }); };
3. 统一RootState类型
修改TodoList.tsx中的RootState引用,使用store导出的类型而非自定义:
import { useSelector } from 'react-redux'; import TodoItem from './TodoItem'; import React from 'react'; // 导入store中导出的RootState import type { RootState } from '../store'; // 导入todoSlice中的Todo类型 import type { Todo } from '../store/todoSlice'; const TodoList: React.FC = () => { const todos = useSelector((state: RootState) => state.todos.todos); return ( <ul> {todos.map((todo) => ( <TodoItem defaultTitle={''} key={todo.id} {...todo} /> ))} </ul> ); }; export default TodoList;
4. 优化TodoItem的Dispatch使用
使用store导出的useAppDispatch替代手动定义Dispatch,获得类型安全:
import { toggleComplete, removeTodosThunk } from '../store/todoSlice'; import React from 'react'; // 导入useAppDispatch import { useAppDispatch } from '../store'; interface TodoItemProps { defaultTitle: string; id: number; title: string; completed: boolean; } const TodoItem: React.FC<TodoItemProps> = ({ id, title, completed }) => { const dispatch = useAppDispatch(); // 使用类型化的dispatch const handleToggleComplete = (): void => { dispatch(toggleComplete({id})); // 无需any断言 }; const handleRemoveTodo = (): void => { dispatch(removeTodosThunk(id)); // 无需any断言 }; return ( <li> <input type='checkbox' checked={completed} onChange={handleToggleComplete} /> <span>{title}</span> <button onClick={handleRemoveTodo}>×</button> </li> ); }; export default TodoItem;
额外说明
如果你的DELETE接口返回空值而非被删除的Todo对象,可以修改removeTodosThunk的返回类型为void,并在fulfilled处理中直接根据传入的id过滤:
export const removeTodosThunk = createAsyncThunk<void, number>( 'todo/removeTodo', async function(id) { await axios.delete(`http://localhost:3004/todos/${id}`); // 无需返回数据 } ) // 对应的reducer处理 .addCase(removeTodosThunk.fulfilled, (state, action) => { // action.meta.arg就是传入的id state.todos = state.todos.filter(todo => todo.id !== action.meta.arg); state.status = 'resolved'; })
内容的提问来源于stack exchange,提问作者красюк виктор
相关产品推荐
相关产品推荐

