issue in updating todo onClick checkbox

Viewed 61

if you know react (using context api) ,you can look into my issue

this is my github repo : https://github.com/nareshkumar1201/todo-react

i am facing issue , where i wanted to update/toggle the completed property in todo list ---

present state of the app is that -one can add todo ,delete todo , and now i am working on the checkbox -on click of checkbox it should update the completed property in todo list , issue is that when i click on checkbox for the first time it is working fine but if is select/uncheck the todo for the 2nd time its not working ... i don,t know where i am going wrong ??

it will a great help if some one look into issue

import React, { Fragment, useContext } from "react";
import PropTypes from "prop-types";
import TodoContext from "../context/TodoContext";

const TodoItem = ({ todo }) => {
  // console.log(todo);

  const todoContext = useContext(TodoContext);
  const { deleteTodo, updateTodo } = todoContext;
  // const { id, todo, completed } = todo;

  **const onChange = (e) => {
    console.log("todoItem: update req", todo.id);
    updateTodo(todo.id);
  };**
  const onClick = () => {
    console.log("todoItem: delete req", todo.id);
    deleteTodo(todo.id);
  };

  return (
    <Fragment>
      <div className="container col-12 todo-item-container">
        <ul className="list-group w-100">
          <li className="list-group-item">
            **<input
              type="checkbox"
              name="todo"
              // value={todo.todo}
              onChange={onChange}
            />**
            <span className="text-info"> {todo.todo}</span>
            <button className="btn float-right ">
              <i
                className="fa fa-pencil-square-o text-info "
                aria-hidden="true"
              ></i>
            </button>
            <button className="btn float-right" onClick={onClick}>
              {" "}
              <i className="fa fa-trash text-info" aria-hidden="true"></i>
            </button>
          </li>
        </ul>
      </div>
    </Fragment>
  );
};

TodoItem.propTypes = {
  todo: PropTypes.object.isRequired,
};

export default TodoItem;

This is state:

import React, { useReducer } from "react";
import TodoContext from "./TodoContext";
import TodoReducer from "./TodoReducer";
import { v1 as uuid } from "uuid";
import {
  ADD_TODO,
  EDIT_TODO,
  DELETE_TODO,
  DISPLAY_TODOS,
  UPDATE_TODO,
} from "./types";

const TodoState = (props) => {
  const initialState = {
    todos: [
      {
        id: 1,
        todo: "Do cook food",
        completed: false,
      },
      {
        id: 2,
        todo: "Clean Room",
        completed: false,
      },
      {
        id: 3,
        todo: "Wash Car",
        completed: false,
      },
    ],
  };

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

  //add todo
  const addTodo = (todo) => {
    todo.id = uuid();
    console.log(todo);
    // todo.completed = false;
    dispatch({ type: ADD_TODO, payload: todo });
  };

  //display todo

  const displayTodos = () => {
    dispatch({ type: DISPLAY_TODOS, payload: state.todos });
  };

  //edit todo

  //delete todo

  const deleteTodo = (id) => {
    console.log("payload id", id);
    dispatch({ type: DELETE_TODO, payload: id });
  };

  //update todo

  **const updateTodo = (id) => {
    console.log("in todoState", id);
    dispatch({ type: UPDATE_TODO, payload: id });
  };**

  return (
    <TodoContext.Provider
      value={{
        todos: state.todos,
        addTodo,
        displayTodos,
        deleteTodo,
        updateTodo,
      }}
    >
      {props.children}
    </TodoContext.Provider>
  );
};

export default TodoState;

This is reducer:

import {
  ADD_TODO,
  EDIT_TODO,
  DELETE_TODO,
  DISPLAY_TODOS,
  UPDATE_TODO,
} from "./types";
// import todoContext from "./TodoContext";

const TodoReducer = (state, action) => {
  switch (action.type) {
    case ADD_TODO:
      return {
        ...state,
        todos: [...state.todos, action.payload],
      };
    case DISPLAY_TODOS:
      return {
        ...state,
        todos: action.payload,
      };
    case DELETE_TODO:
      return {
        ...state,
        todos: state.todos.filter((todoObj) => todoObj.id !== action.payload),
      };
  **case UPDATE_TODO:
      console.log("in reducer :", action.payload);
      return {
        ...state,
        todos: state.todos.map((todoObj) => {
          if (todoObj.id === action.payload) {
            todoObj.completed = !todoObj.completed;
          }
          return todoObj;
        }),
      };**  
    default:
      return state;
  }
};

export default TodoReducer;

Screen images:on click on checkbox for the first time -working fine if click on 2nd todo checkbox its not working

1 Answers

There was issue with your return in UPDATE_TODO.

I have fixed same for you, Do test and let me know.

While making changes to the object in todos array you need to copy it using the spread operator and then make changes and then return the object.

        case UPDATE_TODO:
            console.log("in reducer :", action.payload);
            return {
                ...state,
                todos: state.todos.map((todoObj) => {
                    if (todoObj.id === action.payload) {
                        return { ...todoObj, completed: !todoObj.completed };
                    }
                    return todoObj;
                }),
            };
Related