问题描述
我尝试使用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);