How to delete a list item with React (function component)

Viewed 3642

Just working on a simple to do list for practice and I would like to be able to click on an item on my list to remove it. I think I have it very close but cannot figure out how to get some sort of data from the clicked li to compare it against my array.

App.js

import { useState } from "react";
import "./App.css";
import Newtodo from "./NewTodo/NewTodo";
import TodoList from "./TodoList/TodoList";

let INITIAL_TODOS = [
  { id: "e1", title: "Set up meeting", date: new Date(2021, 0, 14), index: 0 },
  {
    id: "e2",
    title: "Doctor appointment",
    date: new Date(2021, 2, 14),
    index: 1,
  },
  { id: "e3", title: "Work on project", date: new Date(2021, 1, 22), index: 2 },
  { id: "e4", title: "Update resume", date: new Date(2021, 6, 14), index: 3 },
];

const App = () => {
  const [todos, setTodos] = useState(INITIAL_TODOS);

  const deleteItem = (e) => {
    const newTodos = todos.filter((item) => item.index !== 1 /*This works properly with a hardcoded value(1) but how can this be done dynamically as e doesn't seem to have anything useful within it (like e.target.value)*/);
    setTodos(newTodos);
  };

  return (
    <div className="App">
      <Newtodo />
      <TodoList items={todos} handleDelete={deleteItem} />
    </div>
  );
};

export default App;

TodoList.js

import "./TodoList.css";
import Todo from "./Todo";

const TodoList = (props) => {
  return (
    <div className="todo-list">
      <ul>
        {props.items.map((todo, i) => (
          <Todo
            index={i}
            key={todo.id}
            title={todo.title}
            date={todo.date}
            handleDelete={props.handleDelete}
          />
        ))}
      </ul>
    </div>
  );
};

export default TodoList;

Todo.js

import "./Todo.css";

const Todo = (props) => {
  const month = props.date.toLocaleString("en-US", { month: "long" });
  const day = props.date.toLocaleString("en-US", { day: "2-digit" });
  const year = props.date.getFullYear();

  return (
    <li onClick={props.handleDelete} className="todo-item">
      <h2>{props.title}</h2>
      <span>
        {day}, {month}, {year}
      </span>
    </li>
  );
};

export default Todo;

Any help or direction would be greatly appreciated!

3 Answers

You need to pass the index of the element you want to delete. In the delete handler filter by elements with indices not equal to the passed index

const deleteItem = (index) => {
  setTodos(todos => todos.filter((item, i) => i !== index));
};

And in the mapping

const TodoList = (props) => {
  return (
    <div className="todo-list">
      <ul>
        {props.items.map((todo, i) => (
          <Todo
            index={i}
            key={todo.id}
            title={todo.title}
            date={todo.date}
            handleDelete={() => props.handleDelete(i)}
          />
        ))}
      </ul>
    </div>
  );
};

It may be better to use the id property instead.

const deleteItem = (id) => {
  setTodos(todos => todos.filter((item) => item.id !== id));
};

...

const TodoList = (props) => {
  return (
    <div className="todo-list">
      <ul>
        {props.items.map((todo, i) => (
          <Todo
            index={i}
            key={todo.id}
            title={todo.title}
            date={todo.date}
            handleDelete={() => props.handleDelete(todo.id)}
          />
        ))}
      </ul>
    </div>
  );
};

And to avoid the anonymous callback in the child component, declare handleDelete as a curried function.

const deleteItem = (id) => () => {
  setTodos(todos => todos.filter((item) => item.id !== id));
};

...

const TodoList = (props) => {
  return (
    <div className="todo-list">
      <ul>
        {props.items.map((todo, i) => (
          <Todo
            index={i}
            key={todo.id}
            title={todo.title}
            date={todo.date}
            handleDelete={props.handleDelete(todo.id)}
          />
        ))}
      </ul>
    </div>
  );
};

First you need to pass your todo id into handle delete then you can access id there

{ props.items.map((todo, i) => (
          <Todo
            index={i}
            key={todo.id}
            title={todo.title}
            date={todo.date}
            handleDelete={()=> props.handleDelete(todo.id)}
          />
        ))}

You access id here

  const deleteItem = (todoId) => {
     const newTodos = todos.filter((item) => item.id !== todoId;
    setTodos(newTodos);
  };

Add the data-index attribute in your Todo.js

<li data-index={props.index} onClick={props.handleDelete} className="todo-item">
  <h2>{props.title}</h2>
  <span>
   {day}, {month}, {year}
  </span>
</li>

and delete it in deleteTodo

const deleteItem = (e) => {
    const newTodos = todos.filter((item) => +item.index !== +e.currentTarget.dataset.index)
    setTodos(newTodos);
  };
Related