I am building a fullstack todolist app and when trying to update a todo, nothing occurs. As shown in Todo.js, I have tried to manually change the value of todo to test the change. When logging the action.payload to the console, it just return the original value. I am dispatching updateTodo in the handleEdit function. I have also checked from the backend and everything works fine when testing in Postman.
Todo.js
import React, { useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { deleteTodo, getTodos, reset, updateTodo } from "../features/todoSlice";
import { toast } from "react-toastify";
export const Todo = ({ todo, removeTodo }) => {
const [task, setTask] = useState({
todo: "task three test",
completed: todo.completed,
isEditing: todo.isEditing,
});
const dispatch = useDispatch();
const { isSuccess, isError, message, todos } = useSelector(
(state) => state.todo
);
const handleEdit = (c) => {
if (c) {
dispatch(updateTodo({ todoId: todo._id, task }));
}
};
const handleClick = () => {
dispatch(deleteTodo(todo._id));
if (isError) {
toast.error(`${message}`, {
position: "top-right",
autoClose: 1000,
hideProgressBar: false,
closeOnClick: true,
pauseOnHover: false,
draggable: false,
progress: undefined,
theme: "colored",
});
}
if (isSuccess) {
toast.success("Todo deleted", {
position: "top-right",
autoClose: 1000,
hideProgressBar: false,
closeOnClick: true,
pauseOnHover: false,
draggable: false,
progress: undefined,
theme: "colored",
});
}
};
return (
<div className="Todo">
<p>{todo.task}</p>
<div className="todo-actions">
<i className="fa-solid fa-pen-to-square"></i>
<i className="fa-sharp fa-solid fa-trash" onClick={handleClick}></i>
<div className="check-wrapper" onClick={(c) => handleEdit(c)}>
{todo.completed && (
<div className="check">
<i className="fa-sharp fa-solid fa-check"></i>
</div>
)}
</div>
</div>
</div>
);
};
todoService.js
import axios from "axios";
const API_URL = "/api/todos/";
// Create todo
const createTodo = async (todoData) => {
const response = await axios.post(API_URL, todoData);
return response.data;
};
// Get todos
const getTodos = async () => {
const response = await axios.get(API_URL);
return response.data;
};
// Delete todos
const deleteTodo = async (todoId) => {
const response = await axios.delete(API_URL + todoId);
return response.data;
};
// Edit todos
const updateTodo = async (todoId, todoData) => {
const response = await axios.put(API_URL + todoId, todoData);
return response.data;
};
const todoService = {
createTodo,
getTodos,
deleteTodo,
updateTodo,
};
export default todoService;
todoSlice.js
import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
import todoService from "./todoService";
import { toast } from "react-toastify";
const initialState = {
todos: [],
isError: false,
isSuccess: false,
isLoading: false,
message: "",
};
// Create new todo
export const createTodo = createAsyncThunk(
"todos/create",
async (todoData, thunkAPI) => {
try {
return await todoService.createTodo(todoData);
} catch (error) {
const message =
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString();
return thunkAPI.rejectWithValue(message);
}
}
);
// Get todos
export const getTodos = createAsyncThunk(
"todos/getAll",
async (_, thunkAPI) => {
try {
return await todoService.getTodos();
} catch (error) {
const message =
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString();
return thunkAPI.rejectWithValue(message);
}
}
);
// Delete todo
export const deleteTodo = createAsyncThunk(
"todos/delete",
async (id, thunkAPI) => {
try {
return await todoService.deleteTodo(id);
} catch (error) {
const message =
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString();
return thunkAPI.rejectWithValue(message);
}
}
);
// Update todo
export const updateTodo = createAsyncThunk(
"todos/update",
async ({ todoId, todoData }, thunkAPI) => {
try {
return await todoService.updateTodo(todoId, todoData);
} catch (error) {
const message =
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString();
return thunkAPI.rejectWithValue(message);
}
}
);
export const todoSlice = createSlice({
name: "todo",
initialState,
extraReducers: (builder) => {
builder
.addCase(createTodo.pending, (state) => {
state.isLoading = true;
})
.addCase(createTodo.fulfilled, (state, action) => {
state.isLoading = false;
state.isSuccess = true;
state.todos.push(action.payload);
})
.addCase(createTodo.rejected, (state, action) => {
state.isLoading = false;
state.isError = true;
state.message = action.payload;
})
.addCase(getTodos.pending, (state) => {
state.isLoading = true;
})
.addCase(getTodos.fulfilled, (state, action) => {
state.isLoading = false;
state.isSuccess = true;
state.todos = action.payload;
})
.addCase(getTodos.rejected, (state, action) => {
state.isLoading = false;
state.isError = true;
state.message = action.payload;
})
.addCase(deleteTodo.pending, (state) => {
state.isLoading = true;
})
.addCase(deleteTodo.fulfilled, (state, action) => {
state.isLoading = false;
state.isSuccess = true;
state.todos = state.todos.filter((todo) => todo._id !== action.payload);
})
.addCase(deleteTodo.rejected, (state, action) => {
state.isLoading = false;
state.isError = true;
state.message = action.payload;
})
.addCase(updateTodo.pending, (state) => {
state.isLoading = true;
})
.addCase(updateTodo.fulfilled, (state, action) => {
state.isLoading = false;
state.isSuccess = true;
console.log(action.payload);
state.todos = state.todos.map((todo) =>
todo._id === action.payload._id
? {
...todo,
task: action.payload.task,
completed: action.payload.completed,
isEditing: action.payload.isEditing,
}
: todo
);
})
.addCase(updateTodo.rejected, (state, action) => {
state.isLoading = false;
state.isError = true;
state.message = action.payload;
});
},
});
export const { reset } = todoSlice.actions;
export default todoSlice.reducer;
// @desc edit todo / toggle complete / isEditing
// @route PUT /api/todos
// @access Public
const editTodo = asyncHandler(async (req, res) => {
const todo = await Todo.findById(req.params.id);
if (!todo) {
res.status(400);
throw new Error("Todo not found");
}
const updatedTodo = await Todo.findByIdAndUpdate(req.params.id, req.body, {
new: true,
});
res.status(200).json(updatedTodo);
Seems like I needed to set task to todoData.
const handleEdit = (c) => {
if (c) {
dispatch(updateTodo({ todoId: todo._id, todoData: task }));
}
};