MERN栈向Atlas添加Todo后返回主页提示map is undefined
React Context Todo应用返回主页时出现'map is undefined'错误的解决方案
问题根源
问题出在Context的Reducer中状态结构不一致:
- 首次获取数据时,
SET_TODOS将state.todos设置为后端返回的完整响应对象(包含todos数组和message字段) - 添加新Todo时,
CREATE_TODO直接将state.todos设置为数组,破坏了原有的结构 - 返回主页后,代码尝试访问
todos.todos,但此时todos本身就是数组,导致todos.todos为undefined,调用map时抛出错误
解决方案
1. 统一Context状态结构
修改TodoContext.js中的todoReducer,让state.todos始终保持为数组类型:
import { createContext, useReducer } from "react"; export const TodoContext = createContext() export const todoReducer = (state, action) => { switch(action.type) { case 'SET_TODOS': // 直接取响应中的todos数组作为状态值 return { todos: action.payload.todos } case 'CREATE_TODO': // 直接在现有数组前添加新todo(原state.todos已是数组) return { todos: [action.payload, ...state.todos] } default: return state } } export const TodosContextProvider = ({ children }) => { const [state, dispatch] = useReducer(todoReducer, { todos: null }) return ( <TodoContext.Provider value={{...state, dispatch}}> { children } </TodoContext.Provider> ) }
2. 调整Home组件的渲染逻辑
现在state.todos直接是数组,去掉多余的todos.todos访问:
import { useEffect } from "react"; import { useTodoContext } from "../hooks/useTodoContext"; const Home = () => { const { todos, dispatch } = useTodoContext() useEffect(() => { const fetchTodos = async () => { const response = await fetch("/todos/items") const json = await response.json() if(response.ok){ dispatch({ type: "SET_TODOS", payload: json}) } } fetchTodos() }, [dispatch]) if(!todos) return <h1>loading...</h1> return ( <div> <div> {todos.length > 0 ? todos.map((todo) => <h2 key={todo._id}>{todo.todo}</h2>) : <p>暂无Todo</p> } </div> </div> ) } export default Home
3. 确认后端返回格式
确保createTodo接口返回单个Todo对象(而非包含数组的对象),示例后端controller代码:
const createTodo = (req, res) => { const newTodo = new Todo(req.body) newTodo.save() .then(savedTodo => { res.status(201).json(savedTodo) // 返回单个todo对象 }) .catch(err => { res.status(400).json({ message: "创建失败" }) }) }
内容的提问来源于stack exchange,提问作者LeeTGC
相关产品推荐
相关产品推荐

