TypeError Cannot read properties of undefined (reading 'map') when trying to delete item using useReducer

Viewed 140

I am trying to use useReducer hook to add and delete items of a todo list in react.

The weird thing happend when I tried to delete it.

The error message is TypeError Cannot read properties of undefined (reading 'map').

The funny thing is if I changed the action to the same as addItem, it could function but if I change to anythign else like simply console.log it would break and showed the same message TypeError Cannot read properties of undefined (reading 'map').

Can anyone help me to find out what the probelm is? I really appreciate it.

import React, { useState, useReducer } from "react";
import { FaBeer } from "react-icons/fa";

import ToDoItem from "./components/ToDoItem";
import "./App.css";

function App() {
  const [inputText, setInputText] = useState("");
  const [state, dispatch] = useReducer(reducer, [], init);

  function init(initArray) {
    return { todos: initArray };
  }

  function reducer(state, action) {
    switch (action.type) {
      case "addItem":
        return { todos: [...state.todos, action.payload.inputText] };
      case "deleteItem":
         return state.todos.filter((item, index) => {
                return index !== action.payload.id;
              });
        //  return { todos: [...state.todos, action.payload.id] };
            
      default:
        return state.todos;
    }
  } 


  function handleChange(event) {
    const newValue = event.target.value;
    setInputText(newValue);
  }
  
  function addItem(e) {
    e.preventDefault();
    dispatch({ type: "addItem", payload: { inputText: inputText } });
    setInputText("");
  }



  return (
    <div className="container">
      <div className="heading">
        <h1>To-Do List</h1>
      </div>
      <div className="form">
        <form onSubmit={addItem}>
          <input onChange={handleChange} type="text" value={inputText} />
          <button type="submit">
            <span>Add</span>
          </button>
        </form>
      </div>
      <div>
        <ul>
          {state.todos.map((eachItem, index) => (
            <ToDoItem
              key={index}
              id={index}
              text={eachItem}          
              dispatch={dispatch}
            />
          ))}
        </ul>
      </div>
    </div>
  );
}

export default App;

This is the toDoItem component

import React from "react";

import audio from './clickSound.wav';
import CheckBox from "./CheckBox";
import Delete from "./Delete";

function ToDoItem(props) {
  const [clicked, setClicked] = React.useState(false);

  function cross() {
    setClicked((prevValue) => {
      return !prevValue;
    });
  }

  function playSound(){
    const sound = new Audio(audio)
    sound.play()
  }

  
  return (
    <div>
      <CheckBox />
      <label>
        <input type="checkbox" className="normalCheckbox" onClick={()=>{cross();playSound();}} />
        <li style={{ textDecoration: clicked ? "line-through" : "none" }}>
          {props.text}
        </li>
      </label>

      <Delete
        onClick={() =>
          props.dispatch({ type: "deleteItem", payload: { id: props.id } })
        }
      />
    </div>
  );
}

export default ToDoItem;

this is delete component

import React from 'react';

import { AiFillDelete} from "react-icons/ai";


const Delete = props => {
  return <button onClick={props.onClick}type="button"> <AiFillDelete/> </button>
  
};



export default Delete;

replace the action with the same as additem with id, it works

replace the action with console.log, it ran four times and then break with the error message

1 Answers

You are not mutating state properly, because you return array, instead object that contains todos array.

You should do like this:

case "deleteItem":
     return {
       ...state,
       todos: state.todos.filter((item, index) => {
            return index !== action.payload.id;
          }); 
     }

Exception happened in your render part, where you were calling .map, because you overrode the structure of whole reducer state, you end up with state as one array, instead with state as { todos: [....] }

Related