Reputation: 233
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
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>
);
});
Upvotes: 1