user11066242
user11066242

Reputation: 233

Unable to toggle individual item of an Array in React

I am trying to build a simple shopping list app where items are added along with a delete button and could be checked/unchecked. I am trying to toggle an item when it's clicked. The toggle works, but it would apply the changes to all the items in the array. I would only like to toggle the item which is clicked. Here is the code:

App.js

import React, { Component } from 'react'
import Result from './Result';
import Navbar from './Navbar';
import Jumbotron from 'react-bootstrap/Navbar';
import Button from 'react-bootstrap/Button';
import Form from 'react-bootstrap/Form';
import Alert from 'react-bootstrap/Alert';
import SearchResult from './SearchResult';


export default class App extends Component {
    constructor(){
    super();
      this.state={
        addedItems:[],
        emailValue:'',
        searchValue:'',
        searchedItems:[],
        selected:false
      }
      this.handleChange=this.handleChange.bind(this);
      this.handleSubmit=this.handleSubmit.bind(this);
      this.onDelete=this.onDelete.bind(this);
      this.handleToggle=this.handleToggle.bind(this);
      this.handleSearch=this.handleSearch.bind(this);
      this.searchResult=this.searchResult.bind(this);
    }

     handleChange(e){
       this.setState({
         emailValue:e.target.value
       })
     }
     handleSubmit(e){
      e.preventDefault();
      let items_copy= this.state.addedItems;
      items_copy.push(this.state.emailValue)
      console.log("item copy"+items_copy)
      this.setState({
        addedItems:items_copy,
        emailValue:''
      })
      console.log(this.state.addedItems)
     }

     handleSearch(e){
       this.setState({
         searchValue:e.target.value
       },()=>{
         this.searchResult(this.state.searchValue)
       })  
     }

     searchResult(searchValue){
       let searchArray=[]
        this.state.addedItems.map(item=>{

          if(item.find(searchValue)){
            searchArray.push(item)
          }
        })
        this.setState({
          searchedItems:searchArray
        })
     }
     handleToggle(){
      console.log("toggled")
      console.log("selected state"+this.state.selected)
      this.setState({
        selected:!this.state.selected
      })
     }

     onDelete(id){
      let items=this.state.addedItems
      console.log("On Delete")
      console.log(items[id])
      let newArray=items.filter((i,itemNumber)=>id!==itemNumber)
      this.setState({
        addedItems:newArray
      })
     }
  render() {
    console.log("")
    return (
     <div>
          <Navbar/>
          <Jumbotron>
          <Alert variant="success">
        <Alert.Heading>Enter the things you would like to buy </Alert.Heading>
        </Alert>
        </Jumbotron>

        <Form>
          <Form.Group controlId="formBasicEmail">
            <Form.Control value={this.state.emailValue} onChange={this.handleChange}type="email" placeholder="Enter email" />
          </Form.Group>
          {(this.state.addedItems.length>0)?
          <Form.Group controlId="formBasicEmail">
            <Form.Control value={this.state.searchValue} onChange={this.handleSearch}type="text" placeholder="Search for an item..." />
          </Form.Group>:''}
          <Button onClick={this.handleSubmit}variant="primary" type="submit">
            Submit
          </Button>
        </Form>



        {(this.state.addedItems.length>0)?
        <Result item={this.state.addedItems} onDelete={this.onDelete} selected={this.state.selected}handleToggle={this.handleToggle}/>
        :<Alert variant="info">
          Your list is empty
        </Alert>
          }     
      </div>
    )
  }
}

Result.js

import React from 'react'
import ListGroup from 'react-bootstrap/ListGroup'
import Button from 'react-bootstrap/Button'

export default function Result(props) {
    const btnStyle={
        marginTop:-7,
        float:'right'
    };

    const {item,onDelete, handleToggle, selected}=props;
    //let show=item.map(item=>item);
    console.log(item) 
    console.log(selected+"In result")
    let itemShow = item.map((item,i) =>{    
     return ( 
         <li onClick={()=>handleToggle(i)}class="list-group-item"  style={{textDecorationLine: selected?'line-through':'none',textDecorationStyle: selected?'solid':'none', border: selected?' 2px solid green':'1px solid black'}}
         key={i}>{item} 
         <Button onClick={()=>onDelete(i)}style={btnStyle}variant="danger">Delete</Button></li>
        )})

    return (
        <div>
            <ListGroup as="ul">
                 {itemShow}
            </ListGroup>
        </div>
    )
} 

Upvotes: 1

Views: 1871

Answers (1)

Drew Reese
Drew Reese

Reputation: 202605

Toggle Single

Update handleToggle to take the index passed to it and store that in state instead of a boolean.

App.js

this.state={
  ...,
  selected: null,
}

handleToggle (selected) {
  console.log("toggled index", selected);
  console.log("selected state", this.state.selected);
  this.setState({ selected });
}

Update the mapped items to compare the selected index to the current index of the item being rendered.

Results.js

props.items.map((item, i) => {
  const isSelected = selected === i;
  return (
    <li
      key={i}
      onClick={() => handleToggle(i)}
      class="list-group-item"
      style={{
        textDecorationLine: isSelected ? "line-through" : "none",
        textDecorationStyle: isSelected ? "solid" : "none",
        border: isSelected ? "2px solid green" : "1px solid black"
      }}
    >
      {item}
      <Button onClick={() => onDelete(i)} style={btnStyle} variant="danger">
        Delete
      </Button>
    </li>
  );
});

Toggle Multiple

Update handleToggle to take the index passed to it and store that in state instead of a boolean. Change selected state to be an object of indices as keys and selected true/false as value.

App.js

this.state={
  ...,
  selected: {},
}

handleToggle (index) {
  console.log("toggled index", index);
  console.log("selected state", this.state.selected);
  this.setState(prevState => {
    // copy state and update index
    const selected = {...prevState.selected};
    selected[index] = !selected[index];
    return { selected };
  });
}

Update the mapped items to extract the selected value from the current index i of the item being rendered.

Results.js

props.items.map((item, i) => {
  const isSelected = selected[i];
  return (
    <li
      key={i}
      onClick={() => handleToggle(i)}
      className="list-group-item"
      style={{
        textDecorationLine: isSelected ? "line-through" : "none",
        textDecorationStyle: isSelected ? "solid" : "none",
        border: isSelected ? "2px solid green" : "1px solid black"
      }}
    >
      {item}
      <Button onClick={() => onDelete(i)} style={btnStyle} variant="danger">
        Delete
      </Button>
    </li>
  );
});

Edit blissful-curie-1hrxo

Upvotes: 1

Related Questions