Mohamed Essam
Mohamed Essam

Reputation: 85

Making crossoff(Strikethrough) in reactjs

I'm start learning Reactjs, and i try to made my first app (To-do-List), i want help in how to make Strike-through on the completed task when i click the button

//index.js
import axios from 'axios'
import React, { Component } from 'react';
import logo from './logo.svg';
import loading from './loading.gif'
import './App.css';
import ListItem from './listItem';

class App extends Component {
constructor() {
super();
this.state = {
  newTodo: '',
  isEmpty: false,
  editing: false,
  editingIndex: null,
  nofication :null,
  notifyFlag : false,
  notifyEditFlag :false,
  loading : true,
  finish : false,
  todos: []
 };
  this.apiUrl = 'https://5d87dcecfeddff0014e1568d.mockapi.io'
  this.addTodo = this.addTodo.bind(this);
  this.updateTodo = this.updateTodo.bind(this);
  this.deleteTodo = this.deleteTodo.bind(this);
  this.handleChange = this.handleChange.bind(this);
  // this.generateTodoId = this.generateTodoId.bind(this);
  this.displayNofication = this.displayNofication.bind(this);
  this.doneTodo = this.doneTodo.bind(this);
  }

  async componentDidMount() {
  const response = await axios.get(`${this.apiUrl}/todos`);
  setTimeout(()=>{
  this.setState({
  todos: response.data,
  loading : false
  });
  },1000)
  }

 async doneTodo(item, index){
 console.log(item.name.strike());
 const todo = this.state.todos[index];
 const response = await axios.put(`${this.apiUrl}/todos/${todo.id}`,{ 
 })
 this.setState({
  finish : true,
 })

 }

 handleChange(event) {
 this.setState({
  newTodo: event.target.value
  });
  if (/^[\s]*([a-zA-Z0-9]+[\s]*)*$/.test(event.target.value)) {
  this.setState({
      isEmpty : true
  })
  } else {
  this.setState({
    isEmpty : false
   })
   }
  }

  // generateTodoId() {
  //   const lastTodo = this.state.todos[this.state.todos.length - 1];
  //   if (lastTodo) {
  //     return lastTodo.id + 1;
  //   }

  //   return 1;
  // }

  async addTodo() {
  if(this.state.isEmpty){
// const newTodo = {
//   name: this.state.newTodo,
//   id: this.generateTodoId()
// };

const response = await axios.post(`${this.apiUrl}/todos`, {
  name: this.state.newTodo
});


const todos = this.state.todos;
todos.push(response.data);

this.setState({
  todos: todos,
  newTodo: '',
  isEmpty :false,
  nofication:this.displayNofication(),
  notifyFlag:true
  });
  this.displayNofication("todo added successfuly")
  }
  }

  editTodo(index) {
  const todo = this.state.todos[index];
  this.setState({
  editing: true,
  newTodo: todo.name,
  editingIndex: index,
  notifyEditFlag :true,
  });
  }

  async updateTodo() {
   this.setState({
  loading : true
   })
   const todo = this.state.todos[this.state.editingIndex];

  const response = await axios.put(`${this.apiUrl}/todos/${todo.id}`,{
  name : this.state.newTodo
  })

  // todo.name = this.state.newTodo;

  const todos = this.state.todos;
  todos[this.state.editingIndex] = response.data;
  setTimeout(()=>{
  this.setState({
  todos,
  editing: false,
  editingIndex: null,
  newTodo: '',
  loading : false
   });
  },1000)
  this.displayNofication("todo update successfuly");
  }

  async deleteTodo(index) {
 const todos = this.state.todos;
 const todo = todos[index]
 await axios.delete(`${this.apiUrl}/todos/${todo.id}`)
 delete todos[index];
 this.setState({ 
  todos,
  notifyFlag:false
 });
 this.displayNofication("todo deleted successfuly")
 }

 displayNofication(notify){
 this.setState({
  nofication :notify
  })
 setTimeout(()=>{
  this.setState({
    nofication : null,
    notifyEditFlag:false
  });
   }, 2000)

  }

  render() {
   return (
  <div className="App">
    <header className="App-header">
      <img src={logo} className="App-logo" alt="logo" />
      <h1 className="App-title">CRUD React</h1>
    </header>
    <div className="container">
      {
       this.state.notifyEditFlag ? this.state.nofication&&<div className="alert mt-3 alert-info"><p>{this.state.nofication}</p></div>:this.state.notifyFlag ? this.state.nofication&&<div className="alert mt-3 alert-success"><p>{this.state.nofication}</p></div>:this.state.nofication&&<div className="alert mt-3 alert-danger"><p>{this.state.nofication}</p></div>
      }
      <input
        type="text"
        name="todo"
        className="my-4 form-control"
        placeholder="Add a new todo"
        onChange={this.handleChange}
        value={this.state.newTodo}
      />
      <button
        onClick={this.state.editing ? this.updateTodo : this.addTodo}
        className="btn-info mb-3 form-control">
        {this.state.editing ? 'Update todo' : 'Add todo'}
      </button>
      {
        this.state.loading&& <img src={loading} alt="loading Gif"/>
      }
      {
        !this.state.editing &&
        <ul className="list-group">
          {this.state.todos.map((item, index) => {
            return <ListItem  
            key={item.id}
            item={item}
            finish={this.state.finish}
            editTodo={()=>{this.editTodo(index);}}
            deleteTodo={()=>{this.deleteTodo(index);}}
            doneTodo={()=>{this.doneTodo(index);}} 
            />
     })}
        </ul>
      }
    </div>
  </div>
  );
   }
   }

  export default App;

listItem.js

import React from 'react';

 const ListItem = (props) => {
 return <li className="list-group-item">

<button
  className="btn-sm mr-4 btn btn-info"
  onClick={props.editTodo}
>U</button>
{props.finish ?  <strike>{props.item.name}</strike>: `${props.item.name}`}
<button
  className="btn-sm ml-4 btn btn-danger"
  onClick={props.deleteTodo}
>X</button>
<button className="btn-sm ml-4 btn btn-info" onClick={props.doneTodo}>Done!</button>
 </li>;
      };

  export default ListItem;

when i clicked button Done! it make all tasks as completed, i only need specific index (li)tag that i clicked the button done in, so you any one could help me to handel it

Upvotes: 0

Views: 455

Answers (1)

pritam
pritam

Reputation: 2558

What you're trying to achieve is,

  1. Storing the state of a ToDo in finish flag.
  2. When a ToDo is marked 'Done', you're updating the finish flag.

Points to keep in mind -

  1. For a React component, whenever the prop or state changes, the component will re-render.
  2. You can't store state of n ToDo items in a single boolean flag finish.

This is what's happening in your code:

  1. You click on Done for any ToDo item.
  2. Control goes to doneTodo and an axios PUT call is made.
  3. After that, state variable finish is updated.
  4. Since state is updated, your App component renders again.
  5. During the render, for every ListItem, a finish value of true is passed from state.
  6. So every ToDo item, is rendered as Done.

To fix the issue, you could choose to have a state finishStatus as array (similar to todos), and update the array index when a Done is clicked (Also need to pass respective value to finish prop of ListItem.

Something like this - https://codesandbox.io/embed/react-basic-todo-stack-58086171-xkt5t

Upvotes: 1

Related Questions