Search code examples
javascriptreactjsmaterial-uistrikethrough

Strike through any item in a list created using material ui and Reactjs


I am trying to strike through a list item once a user has clicked on that particular item.

I have created a function to conduct the style change

const completed = () =>{
     return document.getElementById("demo").style.textDecoration='line-through'
};

The list is generated as below, i have used material ui library

 <List dense={dense} >
      {items.slice(0).reverse().map(x=> (
           <ListItem key={x.id} button id="demo" onClick={completed} divider>
                 <ListItemText primary={listItems(x)}/>
                 <ListItemSecondaryAction />
           </ListItem>

       ))}                              
</List>

From the code i have written i am able to strike only through the first item of the list. Whenever i add new items , always the only item i am able to strike through is the first item.

I am trying to find a way to apply this to all the elements on the list


Solution

  • It is not a good practice to use document.getElementById() in React because then you are accessing the DOM directly. Instead, you have to use ref.

    From official React documentation

    When to Use Refs There are a few good use cases for refs:

    • Managing focus, text selection, or media playback.
    • Triggering imperative animations.
    • Integrating with third-party DOM libraries.

    But in your case we can easily do this by using React state. I assume that your items is stored in your component state and in a todo item, you need to store whether it is completed or not ( a boolean value ). You can update the code like the following.

    const completed = (id) => {
       /* update the state by changing the completed value of the
          clicked todo item */
       this.setState({
          items : this.state.items.map(item => {
             if(item.id === id){
                item.completed = true;
             }
             return item;
          })
       })
    
    }
    
    <List dense={dense}>
      {this.state.items
        .reverse()
        .map(x => (
          <ListItem
            key={x.id}
            button
            // add a unique id
            id={x.id}
            
            // add a strike style depending on the completed property of the todo item
            style={{ textDecoration : x.completed ? 'line-through' : 'none' }} 
            
            // call completed with the id
            onClick={() => completed(x.id)} 
    
            divider
          >
            <ListItemText primary={listItems(x)} />
            <ListItemSecondaryAction></ListItemSecondaryAction>
          </ListItem>
        ))}
    </List>