jerry
jerry

Reputation: 37

React hooks updating array is not working

I'm currently learning hook, and I'm writing a todo-list:

import './App.css';
import React, {useState} from 'react';
import Item from './components/Item';
function App() {
  const [tasks, setTasks] = useState([]);

  const addTask = (e) => {
    if(e.key === 'Enter'){
      let newTask = {content: e.target.value, completed: false};
      console.log(newTask);
      setTasks(prevTask => {
        return ([...prevTask, newTask]);
      });  
      console.log(tasks);
    }
  }

  const completeTask = (e) =>{
    let newTask = tasks.slice();
    newTask[e].completed = !tasks[e].completed;
    setTasks(newTask);
  }

  const deleteTask = (e) => {
    let newTask = tasks.slice();
    newTask.splice(e);
    setTasks(newTask);
  }

  return (
    <>
      <header className="todo-app__header">
        <h1 className="todo-app__title">todos</h1>
      </header>
      <section className="todo-app__main">
        <input className="todo-app__input" placeholder="What needs to be done?" onKeyDown={addTask}/>
        <ul className="todo-app__list" id="todo-list">
          {tasks.map(item => <Item num = {tasks.indexOf(item)} text={item.content} completed = {item.completed} 
          onClick = {completeTask(tasks.indexOf(item))} delete = {deleteTask(tasks.indexOf(item))}/>)}
        </ul>
      </section>
    </>
  );
}

export default App;

However, adding tasks is not working!! The newTask printed is well, but it doesn't push into the tasks array. The tasks is still empty.

What's the problem? Also, another problem: is it related to useeffect? I don't know what useeffect is used for.

Upvotes: 1

Views: 533

Answers (4)

Samira
Samira

Reputation: 2753

there is no problem

    import React, {useState} from 'react';
function Add() {
    const [tasks, setTasks] = useState([]);
    const addTask = (e) => {
        if(e.key === 'Enter'){
            let newTask = {content: e.target.value, completed: false};
            console.log(newTask);
            setTasks(prevTask => {
               return ([...prevTask, newTask])
            });
            console.log(tasks);
        }
    }

    const completeTask = (e) =>{
        let newTask = tasks.slice();
        newTask[e].completed = !tasks[e].completed;
        setTasks(newTask);
    }

    const deleteTask = (e) => {
        let newTask = tasks.slice();
        newTask.splice(e);
        setTasks(newTask);
    }

    return (
        <>
            <header className="todo-app__header">
                <h1 className="todo-app__title">todos</h1>
            </header>
            <section className="todo-app__main">
                <input className="todo-app__input" placeholder="What needs to be done?" onKeyDown={addTask}/>
                <ul className="todo-app__list" id="todo-list">
                    {tasks.map((item)=> <li>{item.content}</li>)}
                </ul>
            </section>
        </>
    );
}

export default Add;

it works very well and that means your Item component is your problem.

Upvotes: 0

Carlton Lindsay
Carlton Lindsay

Reputation: 656

The issue is that every time your component renders, it executes both completeTask and deleteTask, because you are using function calls as props. You need to be passing in a function object or expression. Instead of telling the component to execute completeTask on click, the function call just executes it right there as soon as the component is rendered.

The problem is with this part of your code:

<ul className="todo-app__list" id="todo-list">
    {tasks.map(item => <Item num = {tasks.indexOf(item)} text={item.content} completed = {item.completed} 
    onClick = {completeTask(tasks.indexOf(item))} delete = {deleteTask(tasks.indexOf(item))}/>)}
 </ul>

The following lines:

delete = {deleteTask(tasks.indexOf(item))}
onClick = {completeTask(tasks.indexOf(item))}

Should be changed to:

delete = {() => deleteTask(tasks.indexOf(item))}
onClick = {() => completeTask(tasks.indexOf(item))}

In normal HTML, it would look like delete="deleteFunction()", but in React, it should be delete={deleteFunction}, because writing a function with parenthesis after it is a function call, not a function expression. If you need to pass in an argument, you can either pass the argument in as a prop on the component, or change the line to delete={() => deleteFunction(arg)}, as the parenthesis and arrow makes it a function expression.

See Handling Events: https://reactjs.org/docs/handling-events.html

Upvotes: 2

Bhumik
Bhumik

Reputation: 52

Here as I can see, you are doing console.log(tasks) in the same function. Try the console.log(tasks) outside function and you will see tasks array with the values you entered.

Upvotes: 0

Ali Yaghoubi
Ali Yaghoubi

Reputation: 1280

Your code works well and there is no problem.

React setState action in async. Try to log it in useEffect.

const App = () = => {
    const [tasks, setTasks] = useState([]);

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

    const addTask = () => {
        const newTask = {...};
        setTasks([...tasks, newTask]);
    }
}

useEffect Docs

Upvotes: 0

Related Questions