Reputation: 429
I have an application to divide between available and non-available coupon, when user clicked available coupon the coupon will go to non-available section (vice versa), everything works fine. But, if I searched the coupon in the search box, the bug happens, turns out I can't get the initial index of the searched coupon (it will always change to index 0)
For instance, Coupon B
should have index 1, but when i type Coupon B
in the search box the index will change to 0, which break my changedHandler
logic. How can i solve this?
You can see my application in Action through this link https://go-coupon-d0fe4.firebaseapp.com/
MainContainer.Jsx
state = {
availableCoupon: ['Coupon A', 'Coupon B', 'Coupon C', 'Coupon D'],
nonAvalailable: ['filter0', 'filter1', 'filter2', 'filter3'],
text: ''
};
searchHandler = text => {
this.setState({ text });
};
changedHandler = i => {
console.log(i);
const r = window.confirm('Use Coupon?');
if (r) {
let newItems = [...this.state.nonAvalailable];
let updatedItems = this.state.availableCoupon.splice(i, 1);
newItems.push(...updatedItems);
this.setState({
nonAvalailable: newItems
});
console.log('THIS IS NEW ITEMS ' + updatedItems);
}
return false;
};
render(){
return(
<SearchBox
searchTerm={this.state.text}
filteredList={this.state.availableCoupon}
searchHandler={this.searchHandler}
/>
<FilteredItem
changed={this.changedHandler}
searchTerm={this.state.text}
availableCoupon={this.state.availableCoupon}
/>
)
}
SearchBox.jsx
<Input
onChange={e => this.props.searchHandler(e.target.value)}
value={this.props.text}
type="text"
name="coupone"
id="exampleCoupon"
placeholder="Coupon Goes Here"
/>
filteredItem.jsx
{this.props.availableCoupon
.filter(
item =>
`${item}`
.toUpperCase()
.indexOf(this.props.searchTerm.toUpperCase()) >= 0
)
.map((item, index) => {
return (
<Col key={index} className="mt-3" md="2" lg="2" sm="3" xs="4">
<Card onClick={() => this.props.changed(index)}>
<CardBody>
<CardTitle>{item}</CardTitle>
</CardBody>
</Card>
</Col>
);
})}
Upvotes: 0
Views: 2139
Reputation: 742
So, the reason it is not working is because the key
attribute that you rely on changes every time you actually search. So you have 2 options:
1 - use objects instead of strings, which is kind of more real-world like. Objects have properties that are unique, and not supposed to change, like id for example, or might be something else. For example [{name: 'ABCDE', id: 1}, {name: 'PQRST', id: 2}]
. And so you build filtering, deleting etc on them.
2 - Stay with using strings for a while, but rather rely on the index of each element of the initial array. Here is what I mean:
{this.props.availableCoupon
.filter(
item =>
`${item}`
.toUpperCase()
.indexOf(this.props.searchTerm.toUpperCase()) >= 0
)
.map((item, index) => {
return (
let ind = this.props.availableCoupon.indexOf(item);
<Col key={index} className="mt-3" md="2" lg="2" sm="3" xs="4">
<Card onClick={() => this.props.changed(ind)}>
<CardBody>
<CardTitle>{item}</CardTitle>
</CardBody>
</Card>
</Col>
);
})}
The second one is a realy quick fix but in real world you usually do not do that. And you probably won't need that as long as in real world we have stable id
of each object and with it.
Upvotes: 1
Reputation: 12018
As you can see, using the index of the array as a key for each item is unreliable, since the item's key will change whenever its position in the array changes, therefore making the key pretty much useless.
What you should do instead is make it so that your items are objects with unique IDs associated with each one, so that the ID of each item will always match with the item you're working with. Wherever you're using index
, use item.id
instead.
availableCoupon: [
{name: 'Coupon A', id: 0},
{name: 'Coupon B', id: 1},
{name: 'Coupon C', id: 2},
{name: 'Coupon D', id: 3},
],
nonAvalailable: [
{name: 'filter0', id: 4},
{name: 'filter1', id: 5},
{name: 'filter2', id: 6},
{name: 'filter3', id: 7},
],
Make sure to update the rest of your code accordingly, e.g. by doing <Col key={item.id}>
, filtering by item.name
instead of item
, and so on.
Upvotes: 1