javascriptcssreactjstext-decorations

How to change the style of a specific list item on click in React.js?


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


Solution

  • 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>
          );
        };