Todo-list using useReducer and MongoDB - updating DB after Action of Reducer?

389 views Asked by At

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;
```
0

There are 0 answers