skip to Main Content

I am a beginner when it comes to coding with React or JS.

I was trying to create a To-Do list wherein I can add or delete tasks (which I have already accomplished). However, I wanted to add another functionality of a ‘Done’ button which will strike through the specific task item on click.

My react code areas of interest look like this:

function Todo(){

    const [tasks, settask] = useState([]);
    const [newtask, setnewtask] = useState("");

    function handle_change(event){
        setnewtask(event.target.value);
    }

    function update_todo(){

        if(newtask!=""){        
            settask(t => [...t, newtask]);
            setnewtask("");}
    }

    function delete_task(index){
        const a = tasks.filter((_, i) => i!=index);
        settask(a);
    }

    function strike(index){
        // const striked = {textDecoration: "line-through"};
        tasks[index].style.textDecoration = "line-through";  
    }


    return(
        <div id="main">
            <h1>To-Do List</h1>
            <div id="add">
                <input type="text" id="add_item" placeholder='Enter your task' value={newtask} onChange={handle_change}></input>
                <input type="button" id="add_button" onClick={update_todo} value="Submit"></input>
            </div>
            <div id="list">
                <ol>{tasks.map((element,index) => <li key={index}>
                    <div className="list_item">{element}</div>
                    <div className="button_container">
                        <button className="delete" onClick={() => delete_task(index)}>Delete</button>
                        <button className="done" onClick={() => {strike(index)}}>Done</button>
                        </div>
                    </li>)}
                </ol>
            </div>
        </div>
    )
}

I have implemented the above code, but the done button functionality is doing nothing, I tried multiple ways but none of them is working.

enter image description here

2

Answers


  1. First of all, what exactly do you store in the tasks state? Looking at update_todo, I may say that tasks is an array of strings. But, in the strike handler you work with tasks as with an array of objects when you try to access the style property. But there are strings there. If you’d like to store additional data in tasks, you have to store objects there. Something like:

    function update_todo(){
        if(newtask!=""){        
            settask(t => [...t, { text: newtask }]);
            setnewtask("");}
    }
    

    Second, the style property is not used while rendering. No magic in the react adds CSS. You have to explicitly add styles to an element you want to style.

    {tasks.map((task,index) => <li key={index}>
        <div className="list_item" style={task.style}>{task.text}</div>
        {/* other code */}
    </li>)}
    

    And last but not least, in the strike handler you mutate a local variable. It does not cause react to rerender. You have to use setState with an updated array of tasks, e.g.

    function strike(index){
        setTasks(tasks => tasks.map((task, i) => {
            // If this is not the task we want to strike, just return the same task object
            if (i !== index) {
                return task;
            }
    
            // Otherwise create a new task object with redefined styles property and return it
            const finishedTask = {
                ...task,
                style: {
                    ...task.style,
                    textDecoration: "line-through",
                },
            }
    
            return finishedTask;
        }))
    }
    

    This concept of setting state instead of modifying local variable is explained in the official react docs: State: A Component’s Memory – React

    Login or Signup to reply.
  2. heres how you could do it:

    function Todo(){
          const [tasks, settask] = useState([]);
          // add completed to state of your tasks
          const [newtask, setnewtask] = useState({ text: '', completed: false });
    
          function handle_change(event) {
            //update only the textfrom the input
            setnewtask({ ...newtask, text: event.target.value });
          }
    
          function update_todo() {
            if (newtask != '') {
              settask((t) => [...t, newtask]);
              // reset entire state of todo
              setnewtask({ text: '', completed: false });
            }
          }
    
          function delete_task(index) {
            const a = tasks.filter((_, i) => i != index);
            settask(a);
          }
    
          function strike(index) {
            // map the correspondant task bu index if match alter the completion
            // if not keep it as it is
            const updatedtasks = tasks.map((task, i) =>
              i === index ? { ...task, completed: !task.completed } : task
            );
            // update your tasks array
            settask(updatedtasks);
          }
    
          return (
            <div id="main">
              <h1>To-Do List</h1>
              <div id="add">
                <input
                  type="text"
                  id="add_item"
                  placeholder="Enter your task"
                  value={newtask.text}
                  onChange={handle_change}
                ></input>
                <input
                  type="button"
                  id="add_button"
                  onClick={update_todo}
                  value="Submit"
                ></input>
              </div>
              <div id="list">
                <ol>
                  {tasks.map((element, index) => (
                    <li key={index}>
                      <div
                        className="list_item"
                        // use style and completed state to change your text decoration
                        style={{
                          textDecoration: element.completed ? 'line-through' : 'none',
                        }}
                      >
                        {element.text}
                      </div>
                      <div className="button_container">
                        <button className="delete" onClick={() => delete_task(index)}>
                          Delete
                        </button>
                        <button
                          className="done"
                          onClick={() => {
                            strike(index);
                          }}
                        >
                          // additionally you can apply it to the text of button also
                          {element.completed ? 'Undone' : 'Done'}
                        </button>
                      </div>
                    </li>
                  ))}
                </ol>
              </div>
            </div>
          );
        };
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search