I tried connecting my Todolist with MongoDB to store my data. Since it is not recommended to fetch and update data within the reducer I'm a little confused on where to update the DB.
My thoughts are whenever I change the List with an Action, I would need to Replace the whole list on the DB, which doesnt seem efficient to me. But when i only update only the changed element in the DB i dont see a reason to use useReducer.
Can someone help me how i should continue? :/
(This Todolist was using useStates and MongoDB before I tried exercising on useReducer, that's why the Routes and APIhelper include other functions)
App.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 = (state, action) => {
switch (action.type) {
case ACTIONS.SET_TODOS: {
return Object.assign({}, state.todos, {
todos: action.payload.todos,
});
}
case ACTIONS.ADD_TODO:
return [...state.todos, newTodo(action.payload.task)];
case ACTIONS.TOGGLE_TODO:
return state.todos.map((todo) => {
if (todo._id === action.payload.id) {
return { ...todo, completed: !todo.completed };
}
return todo;
});
case ACTIONS.DELETE_TODO:
return state.todos.filter((todo) => todo._id !== action.payload.id);
default:
return state.todos;
}
};
const newTodo = (task) => {
return { _id: Date.now(), task: task, completed: false };
};
export const setTodos = (todos) => {
return {
type: ACTIONS.SET_TODOS,
payload: {
todos,
},
};
};
const App = () => {
const initialState = {
todos: [],
};
const [state, dispatch] = useReducer(reducer, initialState);
const [task, setTask] = 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: { task: task } });
setTask("");
};
return (
<div>
{console.log(state.todos)}
<form onSubmit={handleSubmit}>
<input
type="text"
value={task}
onChange={(e) => setTask(e.target.value)}
/>
</form>
{state.todos &&
state.todos.map((todos) => {
return <Todo key={todos._id} todo={todos} dispatch={dispatch} />;
})}
{//APIHelper.updateTodo(state.todos)}
</div>
);
};
export default App;
Todo.js:
import React from "react";
import { ACTIONS } from "../App";
const Todo = ({ todo, dispatch }) => {
return (
<div>
<span style={{ color: todo.complete ? "#AAA" : "#000" }}>
{todo.task}
</span>
<button
onClick={() =>
dispatch({ type: ACTIONS.TOGGLE_TODO, payload: { id: todo.id } })
}
>
Toggle
</button>
<button
onClick={() =>
dispatch({ type: ACTIONS.DELETE_TODO, payload: { id: todo.id } })
}
>
Delete
</button>
</div>
);
};
export default Todo;
APIHelper.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 (payload) => {
const { data: newTodo } = await axios.put(`${API_URL}`, payload);
return newTodo;
};
const getAllTodos = async () => {
const { data: todos } = await axios.get(API_URL);
return todos;
};
export default { createTodo, deleteTodo, updateTodo, getAllTodos };
routes.js:
const db = require("./db.js");
const routes = express.Router();
const success = (res, payload) => {
return res.status(200).json(payload);
};
routes.get("/", async (req, res, next) => {
try {
const todos = await db.Todo.find({}, "_id task completed");
return success(res, todos);
} catch (err) {
next({ status: 400, message: "failed to get todos" });
}
});
routes.post("/", async (req, res, next) => {
try {
const todo = await db.Todo.create(req.body);
return success(res, todo);
} catch (err) {
next({ status: 400, message: "failes to create todo" });
}
});
routes.put("/", async (req, res, next) => {
try {
const todo = await db.Todo.findByIdAndUpdate(req.params.id, req.body, {
new: true,
});
return success(res, todo);
} catch (err) {
next({ status: 400, message: "failed to update todo" });
}
});
routes.delete("/:id", async (req, res, next) => {
try {
await db.Todo.findByIdAndRemove(req.params.id);
return success(res, "todo deleted");
} catch (err) {
next({ status: 400, message: "failed to delete todo" });
}
});
routes.use((err, req, res, next) => {
return res.status(err.status || 400).json({
status: err.status || 400,
message: err.message || "there was an error processing request",
});
});
module.exports = routes;
```