带有useReducer和MongoDB的待办事项列表-错误:“ todo.map不是函数” 1将todos重命名为state 2将状态对象更改为待办事项数组:

问题描述

我尝试使用nodejs和MongoDB建立一个简单的Todolist。

我还使用了useReducer来简化所有操作并更好地理解它。

我不知道哪里出了问题,但出现错误“ todos.map不是函数”。 我想这与数据库列表的获取有关,但我不确定:/

App.js:

df <- structure(list(Name = c("Mark","Susan","Gerald"),Subject = c("English","English","Math"),Marks = c(45L,23L,33L),Check = c("P","F","P")),class = "data.frame",row.names = c(NA,-3L))

APIHelper.js:

import React,{ useState,useEffect,useReducer } from "react";
import APIHelper from "./APIHelper.js";
import Todo from './components/Todo'
import "./index.css";

export const ACTIONS = {
  ADD_Todo: 'add-todo',TOGGLE_Todo: 'toggle-todo',DELETE_Todo: 'delete-todo',SET_Todo: 'set-todos'
}

const reducer = (todos,action) => {
  switch (action.type) {
    case ACTIONS.SET_TodoS: {
      return Object.assign({},todos,{
        todos: action.payload.todos,});
    }
    case ACTIONS.ADD_Todo:
      return ([...todos,newTodo(action.payload.name)])
    case ACTIONS.TOGGLE_Todo:
      return todos.map(todo => {
        if(todo.id === action.payload.id) {
          return { ...todo,complete: !todo.complete}
        }
        return todo;
      })
    case ACTIONS.DELETE_Todo:
      return todos.filter(todo => todo.id !== action.payload.id)
    default: 
      return todos;
    }


}

const newTodo = (name) => {
  return { id: Date.Now(),name: name,complete: false }
}


export const setTodos = (todos) => {
  return {
    type: ACTIONS.SET_TodoS,payload: {
      todos
    },};
};



const App = () => {

  const initialState = {
    todos: []
  };

  
  const [todos,dispatch] = useReducer(reducer,initialState);
  const [name,setName] = useState('');

  useEffect(async () => {
    const fetchTodoAndSetTodo = async () => {
      const todos = await APIHelper.getAllTodos();
      return todos;
    };
    const todos = await fetchTodoAndSetTodo();
    console.log(todos);
    dispatch(setTodos(todos));
  },[]);

  const handleSubmit = (e) => {
    e.preventDefault();
    dispatch({ type: ACTIONS.ADD_Todo,payload: {name: name} })
    setName('')
  }

  return (
    
    <div>
      {console.log(todos)};
      <form onSubmit = {handleSubmit}>
        <input type="text" value={name} onChange = {e => setName(e.target.value)}/>
      
      </form>
      {todos.map(todo => {
        return <Todo key={todo.id} todo={todo} dispatch = {dispatch} />
      })}
    </div>
  )
}

export default App;

Todo.js:


import axios from "axios";

const API_URL = "http://localhost:8080/todos/";

const createtodo = async (task) => {
  const { data: newTodo } = await axios.post(API_URL,{
    task,});
  return newTodo;
};

const deletetodo = async (id) => {
  const message = await axios.delete(`${API_URL}${id}`);
  return message;
};

const updatetodo = async (id,payload) => {
  const { data: newTodo } = await axios.put(`${API_URL}${id}`,payload);
  return newTodo;
};

const getAllTodos = async () => {
  const { data: todos } = await axios.get(API_URL);
  return todos;
};

export default { createtodo,deletetodo,updatetodo,getAllTodos };

我希望有人可以帮助我:(

解决方法

在化简器中,您试图映射状态对象,但是您应该映射状态对象的todos数组:

case ACTIONS.TOGGLE_TODO:
             // todos is actually the state object
      return todos.map(todo => {

您应该:

1。将todos重命名为state

const reducer = (state,action) => {
  switch (action.type) {
    case ACTIONS.SET_TODOS: {
      return {
        ...state,todos: action.payload.todos,};
    }
    case ACTIONS.ADD_TODO:
      return {
        ...state,todos: [...state.todos,newTodo(action.payload.name)],};
    case ACTIONS.TOGGLE_TODO:
      return {
        ...state,todos: state.todos.map((todo) => {
          if (todo.id === action.payload.id) {
            return { ...todo,complete: !todo.complete };
          }
          return todo;
        }),};
    case ACTIONS.DELETE_TODO:
      return {
        ...state,todos: state.todos.filter((todo) => todo.id !== action.payload.id),};
    default:
      return state;
  }
};

或者...

2。将状态对象更改为待办事项数组:

 const initialState = [] // todos array
const reducer = (todos,action) => {
  switch (action.type) {
    case ACTIONS.SET_TODOS: {
      return action.payload.todos;
    }
    case ACTIONS.ADD_TODO:
      return [...todos,newTodo(action.payload.name)];
    case ACTIONS.TOGGLE_TODO:
      return todos.map((todo) => {
        if (todo.id === action.payload.id) {
          return { ...todo,complete: !todo.complete };
        }
        return todo;
      });
    case ACTIONS.DELETE_TODO:
      todos.filter((todo) => todo.id !== action.payload.id);
    default:
      return todos;
  }
};

,

问题

您的初始状态是一个对象

const initialState = {
  todos: []
};

但是您的一些化简案例将状态视为数组

const reducer = (todos,action) => {
  switch (action.type) {
    case ACTIONS.SET_TODOS: {
      return Object.assign({},todos,{
        todos: action.payload.todos,});
    }
    case ACTIONS.ADD_TODO:
      return ([...todos,newTodo(action.payload.name)]) // <-- here
    case ACTIONS.TOGGLE_TODO:
      return todos.map(todo => { // <-- here
        if(todo.id === action.payload.id) {
          return { ...todo,complete: !todo.complete}
        }
        return todo;
      })
    case ACTIONS.DELETE_TODO:
      return todos.filter(todo => todo.id !== action.payload.id) // <-- here
    default: 
      return todos;
    }
}

解决方案

更新reducer逻辑以正确访问待办状态。我建议将其称为“状态”,然后访问state.todos以获取当前状态值。每个案例都需要使用一个todos键返回一个对象。

const reducer = (state,action) => {
  switch (action.type) {
    case ACTIONS.SET_TODOS:
      return {
        todos: action.payload.todos,});
    
    case ACTIONS.ADD_TODO:
      return {
        todos: [...state.todos,newTodo(action.payload.name)]
      }

    case ACTIONS.TOGGLE_TODO:
      return {
        todos: state.todos.map(todo => {
          if(todo.id === action.payload.id) {
            return { ...todo,complete: !todo.complete}
          }
          return todo;
        })
      }

    case ACTIONS.DELETE_TODO:
      return {
        todos: state.todos.filter(todo => todo.id !== action.payload.id)
      }

    default: 
      return state;
    }
}

您还应该更新UI逻辑以反映此嵌套

const [state,dispatch] = useReducer(reducer,initialState);

...

{state.todos.map(todo => {
  return <Todo key={todo.id} todo={todo} dispatch = {dispatch} />
})}

替代解决方案

如果您不打算在化简器状态下存储其他任何内容,那么将状态只是设置为数组可能会容易一些。

更新SET_TODOS操作处理程序,以将操作中的待办事项散布(即复制)到新的数组引用中。

const reducer = (todos,action) => {
  switch (action.type) {
    case ACTIONS.SET_TODOS:
      return [...action.payload.todos]; // <-- spread into array
    
    case ACTIONS.ADD_TODO:
      return [...todos,newTodo(action.payload.name)]

    case ACTIONS.TOGGLE_TODO:
      return todos.map(todo => {
        if(todo.id === action.payload.id) {
          return { ...todo,complete: !todo.complete}
        }
        return todo;
      });
    
    case ACTIONS.DELETE_TODO:
      return todos.filter(todo => todo.id !== action.payload.id)

    default: 
      return todos;
    }
}

initialState更新为一个空数组。

 const initialState = []; // <-- just the array

现在todos 状态,它只是一个数组,因此任何todos.map都可以按您期望的方式工作。

const [todos,initialState);