2

I have a text input search bar to look up products and as I type in terms it makes an AJAX request to find the matches and sets those as the products state. However, it is lagging by one character. IE I type in "car" and it is searching for "ca", when I type in "cars" it searches with "car". I'm guessing it has something to do with how React setState works. Hints on how to solve this?

class ProductIndexPage extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      term: '',
      collection: null,
      products: this.props.products
    };
    this.handleChange = this.handleChange.bind(this);
  }
  handleChange(event) {
    this.setState({term: event});
    this.searchProducts()
  }
  render() {
    function CreateItem(product) {
      return { 
        url: '/products/' + product.id, 
        attributeOne: product.title,
        attributeTwo: product.variants[0].variant_price,
        attributeThree: '5 prices from $8.99 to $13.99',
        media: <Thumbnail
                  source={product.main_image_src}
                  alt={product.title}
                  size="small"
                />
      }
    }
    function CollectionTitles(collection) {
      return collection.title
    }
    return (
      <Layout>
        <Layout.Section>
          <Card title="Online store dashboard" actions={[{content: 'Edit'}]} sectioned>
            <Card.Section>
              <Stack>
                <TextField 
                  value={this.state.term}
                  label="Keyword Search"
                  placeholder="Enter Term"
                  onChange={this.handleChange}
                />
                <Select
                  value= {this.state.collection}
                  label="Collection"
                  options={ this.props.collections.map(CollectionTitles) }
                  placeholder="Select"
                  //onChange={this.valueUpdater('collection')}
                  //onChange={this.searchProducts('collection')}
                />
              </Stack>
            </Card.Section>
            <Card.Section>
                <ResourceList
                  items={
                      this.state.products.map(CreateItem)
                  }
                  renderItem={(item, index) => {
                    return <ResourceList.Item key={index} {...item} className='Polaris-Card' />;
                  }}
                />
            </Card.Section>
          </Card>
        </Layout.Section>
      </Layout>
    ); 
  }
searchProducts() {
  console.log('In queryProducts');
  console.log('AJAX')
  $.ajax( {
    url: '/products/' + "?term=" + this.state.term, //+ "&collection=" + this.state.collection,
    dataType: 'json',
    success: function(data) {
      console.log('SUCCESS');
      console.log(data)
    this.setState({ products: data });
    }.bind(this),
    error: function(data) {
    }.bind(this)
   });
}
ByteMe
  • 1,159
  • 2
  • 15
  • 28

1 Answers1

2

Thats because setState function is asynchronous. So when you call setState then it does not set the state immediatly. And you are calling api call right after calling setState (i.e. when state is not updated yet). Thats why it is calling api without last letter.

The solution:: Pass a callback function to setState. That callback will be called when state is update. In that callback you can call your api like this

handleChange(event) {
    this.setState({term: event}, () => {
      this.searchProducts()
    });
  }
Prakash Sharma
  • 15,542
  • 6
  • 30
  • 37