You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 15:07:57