skip to Main Content

I am trying to make a todo app in which I made a functionality of delete when ever I delete a element , the element is deleted successfully but as soon as I type in input box I get this error.
Failed to execute ‘removeChild’ on ‘Node’: The node to be removed is not a child of this node.

import { React, useState } from 'react'
import "./todo.css"

function Todo() {
    const [value, setValue] = useState("")  //input value
    const [items, setitems] = useState([])  //items to be added

    function handleClick() {
        //  Adding items in a item array
        setitems(items.concat(value))
        setValue("")
    }

    function handleChange(e) {
        // Fetching input value
        setValue(e.target.value)
    }



    return (
        <>
            <div className='container'>
                <h1 id='mainhead'>Todo App</h1>
            </div>

            <div className='container1'>
                <input onChange={handleChange} value={value} placeholder='Enter a  task' type="text" />
                <button onClick={handleClick}>Add Item</button>
            </div>

            {/* mapping all the items */}

            {items.length !== 0 ? items.map((e) => {
                return <div className='item' key={e}><label>Task {items.indexOf(e) + 1}:</label> {e}

                    <button style={{float:"right" , backgroundColor:"red" , color:"white" , width:"80px" , height:"30px"}} onClick={()=>{

                        const child = document.getElementById("delete" + items.indexOf(e)) // accessing child
                        console.log(child)
                        child.parentElement.remove() //Deleting parent element
                        items.splice(items.indexOf(e),1) //removing element from items
                        setitems(items)   // updating items

                    }} id = {"delete" + items.indexOf(e)}>Delete</button> </div>
            })
                : null}



        </>
    )
}

export default Todo

I tried everything but nothing works help me to handle this error

2

Answers


  1. Instead of directly change the DOM, use the state to manage the items. To remove an item from the list, you can filter the item and set the new state.

    function handleDelete(index) {
      // Filter the items array and remove the item at the given index
      const newItems = items.filter((_, i) => i !== index);
      setItems(newItems);
    }
    

    Replace the onClick handler for the Delete button with the handleDelete() function. So the rendering will change this way:

    {/* mapping all the items */}
    {
      items.length !== 0
        ? items.map((e, index) => {
            return (
              <div className="item" key={index}>
                <label>Task {index + 1}:</label> {e}
                <button
                  style={{
                    float: "right",
                    backgroundColor: "red",
                    color: "white",
                    width: "80px",
                    height: "30px",
                  }}
                  onClick={() => handleDelete(index)}
                  id={"delete" + index}
                >
                  Delete
                </button>
              </div>
            );
          })
        : null;
    }
    

    BTW: The code above uses indexes for filtering and as a virtual DOM element key just to get as close as possible to your example. It’s a good practice to have unique IDs for elements. This can improve performance, help avoid errors, and make debugging easier.

    Login or Signup to reply.
  2. You basically have all the bits you need for your component – most importantly the state that holds the todos. The necessary change is that the handler that is called when you click any of the delete buttons should simply filter out the right todo from the state.

    The best way to facilitate this is to ensure that the todos you add to the state are objects, and each object has an id. You then add that id to a data attribute so that when the handler is called you can use that id to filter the correct object from the state.

    I’ve played around with your code a little – renamed things, and put the todos in a table – but hopefully the comments will help.

    const { useState } = React;
    
    function Todos() {
      
      const [ text, setText ] = useState('');
      const [ todos, setTodos ] = useState([]);
    
      // You should be setting state without
      // mutating the array. Here I'm adding a new
      // object with an id to the state (we pass in 
      // the previous state, and then add the new object
      // to that
      function handleAdd(e) {
        setTodos(prev => {
          return [
            ...prev,
            { id: prev.length + 1, text }
          ];
        });
        setText('');
      }
    
      function handleChange(e) {
        setText(e.target.value);
      }
    
      // You can use the id from the element's dataset
      // to allow you to filter out that object from the todos
      // array, and then set the todos state with that 
      // filtered array. Note the id from the dataset will be a
      // string so we need to coerce it to a number before we
      // do a strict equality comparison
      function handleDelete(e) {
        const { dataset: { id } } = e.target;
        const filtered = todos.filter(todo => Number(id) !== todo.id);
        setTodos(filtered);
      }
    
      // For this example I've put the todos in a table
      // for easier visualisation. You'll note that the todo
      // id is added to a data attribute on the each delete button
      // so that it can be used in the delete handler
      return (
        <main>
    
          <header>
            <h1>Todo App</h1>
          </header>
    
          <section className="container1">
            <input onChange={handleChange} value={text} placeholder="Enter a  task" type="text" />
            <button onClick={handleAdd}>Add Item</button>
          </section>
    
          <table>
            <tbody>
            {todos.length ? todos.map(todo => {
              return (
                <tr className="item" key={todo.id}>
                  <td class="id">{todo.id}</td>
                  <td class="text">{todo.text}</td>
                  <td>
                    <button
                      data-id={todo.id}
                      class="delete"
                      onClick={handleDelete}
                    >Delete
                    </button>
                  </td>
                </tr>
              );
            }) : null}
            </tbody>
          </table>
        </main>
    
      );
    
    }
    
    ReactDOM.render(
      <Todos />,
      document.getElementById('react')
    );
    table { width: 80%; margin-top: 1em; }
    td.id { width: 10%; }
    td.text { width: 100%; }
    td.delete { float: right; }
    .item { display: flex; flex-direction: row; border: 1px solid lightgray; }
    .delete { background-color: salmon; margin-left: 1em; }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
    <div id="react"></div>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search