Mirza Chilman
Mirza Chilman

Reputation: 429

React how to get index in search

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

Answers (2)

seethrough
seethrough

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

kingdaro
kingdaro

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

Related Questions