Home > Blockchain >  Use state doesn't delete from array
Use state doesn't delete from array

Time:11-28

as in title. I am new in the react and I write simple todoApp. My App.js:

  const deleteTask = (index) =>
  {
    let cp =tasks.filter(x=>x.id !== index);
    setTasks(cp);
    console.log(tasks);
  };


  const addTask =(text) => 
  {
    let newTask ={id:tasks.length 1,text:text};
    setTasks([...tasks,newTask]);
  }

  return (
    <Router>
    <div className='container'>
      <Header title='Titlee'/>
      <AddTasks addTask={addTask}></AddTasks>
      <Routes>
      <Route path='/' element=
      {
        <>
        {tasks.length > 0 ? (
                  <Tasks
                    tasks={tasks}
                    onDelete={deleteTask}
                    toggle={toggleReminder}
                  />
                ) : (
                  'No Tasks To Show'
                )
      }
          </>
        }></Route>
      <Route path='/about' element={<About />} ></Route>   
      </Routes>       
     <Footer></Footer>           
    </div>
    </Router>
  )
}

export default App;

My Tasks:

const Tasks =({tasks, onDelete, toggle}) => {
    return (
        tasks.map((task) => (
            <Task key={task.id} task={task} onDelete={onDelete} toggle={toggle}/>
          ))
      )
}

export default Tasks

and my Task.js

const Task = ({ task, onDelete,toggle }) => {
  return (
    <div className='task' onClick={()=>toggle(task.id)} key={task.id}>
      <h3>{task.text} 
      <FaTimes 
        style={{color: 'red', cursor: 'pointer'}} 
        onClick={()=>onDelete(task.id)}/>
        </h3>
      <p>{task.id}</p>
    </div>
  )
}

export default Task

I have init state with 3 hardcoded tasks in App.js. Adding new tasks works proper, and tasks are succesfully updated. The problem is with deleteTask - in 'cp' collection I have updated list of tasks, but console.log (fired just after setTasks) is shows not updated collection. Why? What is improperly done, and how to explain this bug? Moreover - lists of my tasks are not updated (in html) - why? Regards

CodePudding user response:

Because setTasks updates the state value in a newly rendered version of the component, it is a normal behavior that the console.log(tasks) in the same block does not receive an updated copy of tasks.

On a side note, a more standard way of setting state based on a previous value could be:

const deleteTask = (index) =>
  setTasks((prev) => prev.filter((x) => x.id !== index));

Moreover, the current addTask could possibly create a conflict between id and index, and it could be safer like this:

const addTask = (text) =>
  setTasks((prev) => {
    if (!Array.isArray(prev) || prev.length === 0) return [{ id: 1, text }];
    return [...prev, { id: prev[prev.length - 1].id   1, text }];
  });

Here is an over simplified example so that it might be easier to visualize. It runs in the below snippet for convenience.

Hopefully it will help.

const Task = ({ task, onDelete }) => {
  return (
    <div className="task" key={task.id}>
      <h3>
        {task.text}
        <button
          style={{ color: "red", cursor: "pointer" }}
          onClick={() => onDelete(task.id)}
        >
          delete
        </button>
      </h3>
    </div>
  );
};

const Tasks = ({ tasks, onDelete }) => {
  return tasks.map((task) => (
    <Task key={task.id} task={task} onDelete={onDelete} />
  ));
};

const AddTasks = ({ addTask }) => {
  const inputRef = React.useRef(null);
  const handleSubmit = (e) => {
    e.preventDefault();
    const { value } = inputRef.current;
    if (!value) return;
    addTask(value);
  };
  return (
    <form className="add" onSubmit={handleSubmit}>
      <input type="text" ref={inputRef} />
      <button type="submit">Add Task</button>
    </form>
  );
};

const App = () => {
  const [tasks, setTasks] = React.useState([
    { id: 1, text: "Task 1" },
    { id: 2, text: "Task 2" },
    { id: 3, text: "Task 3" },
  ]);

  const deleteTask = (index) =>
    setTasks((prev) => prev.filter((x) => x.id !== index));

  const addTask = (text) =>
    setTasks((prev) => {
      if (!Array.isArray(prev) || prev.length === 0) return [{ id: 1, text }];
      return [...prev, { id: prev[prev.length - 1].id   1, text }];
    });

  return (
    <div className="app">
      <div className="container">
        <AddTasks addTask={addTask}></AddTasks>
        {tasks.length > 0 ? (
          <Tasks tasks={tasks} onDelete={deleteTask} />
        ) : (
          "No Tasks To Show"
        )}
      </div>
    </div>
  );
};

ReactDOM.render(<App />, document.querySelector("#root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.1.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.1.0/umd/react-dom.production.min.js"></script>

CodePudding user response:

React setState is async (read more here) so when you log your state just after setState, it shows the previous data not the updated one because it takes some time for the state to get updated.

You can see that your state is updated by writing an useEffect and log your state there:

const deleteTask = (index) =>
{
  const cp = tasks.filter(x=> x.id !== index);
  setTasks(cp);
};

useEffect(() => {
  console.log(tasks);
}, [tasks]);

CodePudding user response:

In App.js

I would change index to id:

const deleteTask = (id) =>   {
      let cp =tasks.filter((task) => task.id !== id);
      setTasks(cp);
      console.log(tasks);   }; 

and in Task.js:

const Task = ({ id, task, onDelete,toggle }) => {
  return (
    <div className='task' onClick={()=>toggle(task.id)} key={task.id}>
      <h3>{task.text} 
      <FaTimes 
        style={{color: 'red', cursor: 'pointer'}} 
        onClick={()=>onDelete(id)}/>
        </h3>
      <p>{task.id}</p>
    </div>
  )
}

export default Task

Make sure to pass the id as a prop in Tasks.js too.

  • Related