我正在开发一个执行CRUD操作的MERN堆栈"待办事项列表"应用程序.当我从列表中编辑"任务"时,单击保存按钮后我会收到Uncaught TypeError: tasks.map is not a function.我在这里读过类似的主题,并try 了多种解决方案,但仍然无法修复它.

我正在分享相关的代码片段,希望有人能帮助我.您的时间和精力将不胜感激.

在我的100文件夹中,SaveTask.jsx组件:

import React, { useState, useEffect, useContext } from 'react';
import TaskContext from '../context/TaskContext';
import TokenContext from '../context/TokenContext';
import axios from "../axios/axios";
import "../styles/saveTask.css";

function SaveTask() {
    const { tasks, dispatch } = useContext(TaskContext);
    const { userToken } = useContext(TokenContext);

    const [title, setTitle] = useState("");
    const [description, setDescription] = useState("");
    const { editMode, taskToEdit } = tasks; // Extract editMode and taskToEdit from context

    useEffect(() => {
        // Populate form fields with task details when in editMode
        if (editMode && taskToEdit) {
            setTitle(taskToEdit.title);
            setDescription(taskToEdit.description);
        } else {
            setTitle(""); // Reset title when not editing
            setDescription(""); // Reset description when not editing
        }
    }, [editMode, taskToEdit]);

    const handleAddOrEdit = async (e) => {
        e.preventDefault();

        try {
            if (editMode && taskToEdit) {
                // Update existing task
                const res = await axios.post(`/task/editTask/${taskToEdit._id}`, { title, description }, {
                    headers: {
                        Authorization: `Bearer ${userToken}`
                    }
                });
                console.log("Task edited:", res.data);
                // Update task in context
                dispatch({
                    type: 'EDIT_TASK',
                    _id: taskToEdit._id,
                    title: res.data.task.title,
                    description: res.data.task.description
                });

                dispatch({ type: 'CLEAR_EDIT_MODE' }); // Clear edit mode after submission
            } else {
                // Add new task
                const res = await axios.post("/task/addTask", { title, description }, {
                    headers: {
                        Authorization: `Bearer ${userToken}`
                    }
                });
                console.log("New task added:", res.data);
                // Add new task to context
                dispatch({
                    type: "ADD_TASK",
                    _id: res.data.task._id,
                    title: res.data.task.title,
                    description: res.data.task.description,
                });
            }
            // Reset form fields
            setTitle("");
            setDescription("");

        } catch (error) {
            console.log(error);
        }
    };

    return (
        <div className="addContainer md:w-1/3 md:mx-auto mx-3 mt-3 flex justify-center">
            <div className='w-11/12'>
                <form onSubmit={handleAddOrEdit}>
                    <div>
                        <label htmlFor="title">Title</label>
                        <input
                            type="text"
                            name="title"
                            id="title"
                            value={title}
                            required
                            onChange={(e) => setTitle(e.target.value)}
                            className='bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500'
                        />
                    </div>
                    <div className='my-3'>
                        <label htmlFor="description">Description</label>
                        <textarea
                            rows={5}
                            name="description"
                            id="description"
                            value={description}
                            required
                            onChange={(e) => setDescription(e.target.value)}
                            style={{ resize: "none" }}
                            className='bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500'
                        />
                    </div>
                    <div className='flex justify-center'>
                        <button
                            type='submit'
                            className='bg-blue-700 rounded-md text-white px-5 py-1'
                        >
                            {editMode ? "Save" : "Add"}
                        </button>
                    </div>
                </form>
                <div className="toast bg-green-600 text-white p-3 rounded-xl shadow-2xl text-center absolute bottom-4 left-1/2 -translate-x-1/2" id='toast'>
                    <p>This is test</p>
                </div>
            </div>
        </div>
    );
}

export default SaveTask;

Task.jsx成分:

import React, { useContext } from 'react';
import moment from 'moment';
import "../styles/task.css";
import axios from "../axios/axios.js"
import TaskContext from '../context/TaskContext.js';
import TokenContext from '../context/TokenContext.js';
import DeleteIcon from '@mui/icons-material/Delete';
import EditIcon from '@mui/icons-material/Edit';

function Task({ task }) {
    const { _id, title, description, completed } = task;
    const { dispatch } = useContext(TaskContext);
    const { userToken } = useContext(TokenContext);

    const handleRemove = async (e) => {
        e.preventDefault();
        console.log("Task ID to remove:", _id);
    
        try {
            const res = await axios.get("/task/removeTask", {
                headers: {
                    Authorization: `Bearer ${userToken}`
                },
                params: {
                    _id
                }
            });
            console.log("Task deletion response:", res.data);
            dispatch({
                type: "REMOVE_TASK",
                _id
            });
        } catch (error) {
            console.error("Error removing task:", error);
            // Handle error state or display an error message to the user
        }
    }
    
    const handleMarkDone = async () => {
        try {
            const res = await axios.post('/task/updateTaskStatus', {
                _id: task._id,
                completed: !task.completed // Toggle completion status
            }, {
                headers: {
                    Authorization: `Bearer ${userToken}` // Include JWT token in headers
                }
            }
        );
            console.log('Task completion status successfully updated:', res.data);
            dispatch({
                type: 'MARK_DONE',
                _id: task._id
            });
        } catch (error) {
            console.error('Error updating task:', error);
            // Handle error state or display an error message to the user
        }
    };

    const handleEdit = () => {
        console.log('Editing task:', { task }); // Log the task details being edited
        // Dispatch an action to set the task for editing
        console.log('Dispatching SET_EDIT_MODE with task:', task);
        dispatch({
            type: 'SET_EDIT_MODE',
            payload: task // Send the entire task object for editing
        });
    };

... // Rest of the code for UI, etc.

export default Task;

AllTask.jsx成分:

此文件也返回了相同的TypeError: tasks.map is not a function.但我添加Array.isArray(tasks)后就修复了.我无法使其适应当前的错误.

import React, { useContext } from 'react';
import Task from './Task';
import TaskContext from '../context/TaskContext';

function AllTask() {
    const { tasks, editMode, taskToEdit } = useContext(TaskContext);
    
    console.log('Tasks:', tasks);
    console.log('EditMode:', editMode);
    console.log('TaskToEdit:', taskToEdit);
    
    return (
        <div>
            {Array.isArray(tasks) && tasks.length !== 0 ? (
                tasks.map((task, index) => (
                    <Task key={index} task={task} id={index} />
                ))
            ) : (
                <h1>No Tasks Found</h1>
            )}
        </div>
    );
}

export default AllTask;

taskReducer.js个文件:taskReducer.js个文件:

看来case "EDIT_TASK":中的task.map才是错误的根源.case "MARK_DONE":具有相同的 struct ,但成功工作.

function taskReducer(tasks = [], action) {
    console.log("taskreducer");
    switch (action.type) {
        // eslint-disable-next-line no-lone-blocks
        case "ADD_TASK": {
            return [
                ...tasks,
                {
                    _id: action._id,
                    title: action.title,
                    description: action.description,
                    completed: false
                }
            ]
        }
        case "SET_TASK": {
            return action.payload
        }
        case "REMOVE_TASK": {
            console.log("Tasks before removal:", tasks);
            const updatedTasks = tasks.filter((task) => task._id !== action._id);
            console.log("Tasks after removal:", updatedTasks);
            return updatedTasks;
        }
        case "MARK_DONE": {
            return tasks.map((task) => {
                if (task._id === action._id) {
                    return {
                        ...task,
                        completed: !task.completed
                    }
                }
                return task
            })
        }
        case "EDIT_TASK": {
            return tasks.map((task) => {
                if (task._id === action._id) {
                    return {
                        ...task,
                        title: action.title,
                        description: action.description
                    };
                }
                return task;
            });
        }
        case 'SET_EDIT_MODE': {
            console.log('Processing SET_EDIT_MODE:', action.payload);
            return {
                ...tasks,
                taskToEdit: action.payload, // Set the task for editing
                editMode: true // Set edit mode to true
            };
        }
        case "CLEAR_EDIT_MODE": {
            console.log('Processing CLEAR_EDIT_MODE');
            return {
                ...tasks,
                taskToEdit: null,
                editMode: false
            };
        }
        default: {
            throw Error("Unknown Action" + action.type)
        }
    }
}

export default taskReducer;

在我的100文件夹中,taskController.js文件:

import taskModel from "../models/taskModel.js";
import userModel from "../models/userModel.js";
import dotenv from "dotenv";
import mongoose from "mongoose";
dotenv.config();

const addTask = async (req, res) => {
    const { _id, title, description } = req.body;
    const userId = req.user.id;

    const user = await userModel.find({_id: userId});
    if (!user) {
        return res.status(404).json({ message: "User not found" });
    }

    const taskId = _id ? mongoose.Types.ObjectId(_id) : new mongoose.Types.ObjectId();

    console.log("Task to be saved:", { taskId, title, description, completed: false, userId });

    const newTask = new taskModel({ _id: taskId, title, description, completed: false, userId })

    newTask.save()
        .then((savedTask) => {
            return (res.status(200).json({ message: "Task added successfully", task: savedTask }))
        })
        .catch((error) => {
            return (
                res.status(500).json({ message: error.message })
            )
        }
        )
}

const removeTask = (req, res) => {
    const { _id } = req.query;

    console.log("Task ID to remove:", _id); // Log the ID being used for deletion

    taskModel.findByIdAndDelete( _id )
        .then((deletedTask) => {
            if (!deletedTask) {
                return res.status(404).json({ message: "Task not found" });
            }
            console.log("Deleted task:", deletedTask); // Log the deleted task
            return res.status(200).json({ message: "Task deleted successfully" });
        })
        .catch((error) => {
            console.error("Error deleting task:", error); // Log any errors
            return res.status(500).json({ message: "Internal server error" });
        });
}

const getTask = (req, res) => {
    taskModel.find({ userId: req.user.id })
        .lean() // Convert Mongoose documents to plain JavaScript objects
        .then((data) => res.status(200).json(data))
        .catch((error) => res.status(501).json({ message: error.message }))
}


const updateTaskStatus = async (req, res) => {
    const { _id, completed } = req.body;

    try {
        // Find the task by _id and update the completed field
        const updatedTask = await taskModel.findByIdAndUpdate(_id, { completed }, { new: true });

        if (!updatedTask) {
            return res.status(404).json({ message: 'Task not found' });
        }

        res.json(updatedTask); // Return the updated task
    } catch (error) {
        console.error('Error updating task completion:', error);
        res.status(500).json({ message: 'Failed to update task completion status' });
    }
};


const editTask = async (req, res) => {
    const _id = req.params._id;
    const { title, description } = req.body;

    try {
        const updatedTask = await taskModel.findByIdAndUpdate(
            _id,
            { title, description },
            { new: true }
        );

        if (!updatedTask) {
            return res.status(404).json({ message: "Task not found" });
        }

        return res.status(200).json({ message: "Task edited successfully", task: updatedTask });
    } catch (error) {
        console.error("Error updating task:", error);
        return res.status(500).json({ message: "Internal server error" });
    }
};


export { addTask, getTask, removeTask, updateTaskStatus, editTask }

当我保存已编辑的任务时(任务被成功编辑并保存到数据库),这就是我在browser console上遇到的情况:

XHR OPTIONS http://localhost:8000/api/task/editTask/663502dc2799787a594e2d6e [HTTP/1.1 204 No Content 3ms]

XHR POST http://localhost:8000/api/task/editTask/663502dc2799787a594e2d6e [HTTP/1.1 200 OK 221ms]

Task edited: Object { message: "Task edited successfully", task: {…} } SaveTask.jsx:37

taskreducer taskReducer.js:2

Uncaught TypeError: tasks.map is not a function
    taskReducer taskReducer.js:37
    React 3
    App App.js:19
    React 11
    workLoop scheduler.development.js:266
    flushWork scheduler.development.js:239
    performWorkUntilDeadline scheduler.development.js:533
    js scheduler.development.js:571
    js scheduler.development.js:633
    factory react refresh:6
    Webpack 24

推荐答案

感谢用户@ onur-doguan带领我找到了解决方案.

我用console.log声明判断了tasks值,该声明返回为Tasks before mapping: Object { 0: {…}, 1: {…}, 2: {…}, 3: {…}, 4: {…}, 5: {…}, 6: {…}, taskToEdit: {…}, editMode: true }

然后我在taskReducer.js中编辑了case "EDIT_TASK":,以调整如何作为对象访问和更新任务:

        case "EDIT_TASK": {
            const { taskToEdit, title, description } = action;
        
            if (tasks && tasks.taskToEdit) {
                const updatedTask = {
                    ...tasks.taskToEdit,
                    title,
                    description
                };
        
                return {
                    ...tasks,
                    taskToEdit: {
                        ...tasks.taskToEdit,
                        title,
                        description
                    },
                    [taskToEdit]: updatedTask
                };
            }
        
            return tasks;
        }

现在"编辑和保存任务"按预期工作,如我的浏览器控制台上所示:

XHR OPTIONS http://localhost:8000/api/task/editTask/663502dc2799787a594e2d6e [HTTP/1.1 204 No Content 3ms]

XHR POST http://localhost:8000/api/task/editTask/663502dc2799787a594e2d6e [HTTP/1.1 200 OK 205ms]

Task edited: Object { message: "Task edited successfully", task: {…} } SaveTask.jsx:37

taskreducer taskReducer.js:2

Processing CLEAR_EDIT_MODE taskReducer.js:70

Javascript相关问答推荐

如何在Node.js中减go 两个日期和时间?

Regex可以 Select 以任何顺序匹配组

为什么我无法使用useMemo()停止脚本渲染?

如何使用JavaScript用等效的功能性HTML替换标记URL格式?

如何为GrapesJS模板编辑器创建自定义撤销/重复按钮?

如何在Angular中插入动态组件

Chrome是否忽略WebAuthentication userVerification设置?""

如何修复(或忽略)HTML模板中的TypeScript错误?'

如何使用JavaScript将文本插入空div

构造HTML表单以使用表单数据创建对象数组

显示图—如何在图例项上添加删除线效果?

单个HTML中的多个HTML文件

在forEach循环中获取目标而不是父对象的属性

try 使用javascript隐藏下拉 Select

Reaction Redux&Quot;在派单错误中检测到状态Mutations

如何在DYGRAPS中更改鼠标事件和键盘输入

对具有相似属性的对象数组进行分组,并使用串连的值获得结果

将Auth0用户对象存储在nextjs类型脚本的Reaction上下文中

在Java脚本中录制视频后看不到曲目

是否可以在不更改组件标识的情况下换出Reaction组件定义(以维护状态/引用等)?如果是这样的话,是如何做到的呢?