0

I have a component which contains the handler function for when a user inserts something into a text box. When this is done, the query field in the state object should be set to the value of the text box.

For example, if the text box is empty, and then the user inputs "s", in handleInput(), I will do this.setState({event.target.value}), which should set this.state.query to "s". But this doesn't work.

When I input "s", the line alert("setting query to "+this.state.query); shows me "setting query to", instead of the expected "setting query to s". This is really strange to me, because the line alert(event.target.value) prints out "s", so the value is definitely in event.target.value.

Then if I input another character, say "a", the alert shows me "setting query to s". It's as if this.state.query is lagging behind by 1 character all the time. Here's the code:

class Search extends Component {
  constructor(props){
    super(props);
    let today = new Date();
    this.state = {
      searchSuggestion: 'Search for tweets here',
      anchorEl: null,
      date: today.toJSON(),
      geocode: undefined,
      page: 0,
      placeName: 'the World',
      query: ''
    }
    // Defining debounce is needed in constructor 
    this.searchTweets = debounce(500, this.searchTweets);
    this.searchGeocode = debounce(500, this.searchGeocode);
  }

  componentDidMount() {
    modelInstance.addObserver(this);
  }

  handleInput = event => {
    alert("Handling input");
    let query = event.target.value;
    alert(event.target.value); // "s"
    this.setState({
      query: event.target.value
    });
    alert("setting query to "+this.state.query); // outputs "setting query to". this.state.query isn't set to anything!
    let until = new Date(this.state.date);
    this.searchTweets(query, this.state.geocode, until);
  }

  // Searches tweets until date
  searchTweets = (query, location, date) => {
    this.props.handleStatusChange('INITIAL');
    modelInstance.searchTweets(query, location, date).then(result => {
      modelInstance.setTweets(result);
      this.props.handleStatusChange('LOADED');
      this.setState({
        data: result
      });
    }).catch(() => {
      this.props.handleStatusChange('ERROR');
    });
  }

  render(){
    return(
        <div className='search'>
          <Row id='searchInput'>
            <SearchInput handleInput={this.handleInput.bind(this)} searchInput={this.state.searchInput} searchSuggestion={this.state.searchSuggestion} page={1}/>
          </Row>
          <Row>
            <SearchNav page={this.state.page}/>
          </Row>
          <Row id='date-location'>
            <Col xs={2} sm={2} md={2} className='text'>
              <p>UNTIL</p>
            </Col>
            <Col xs={4} sm={4} md={4} className='date'>
              <SearchDate date={this.state.date} anchorEl={this.state.anchorEl} click={this.handleClick} dayChange={this.onDayChange}/>
            </Col>
            <Col xs={2} sm={2} md={2} className='text'>
              <p>IN</p>
            </Col>
            <Col xs={4} sm={4} md={4} className='location'>
              <SearchLocation placeName = {this.state.placeName} handleLocation={this.handleLocation.bind(this)}/>
            </Col>

          </Row>
        </div>
    )
  }
}

export default Search;

How come this happens?

JJWesterkamp
  • 7,559
  • 1
  • 22
  • 28
Sahand
  • 7,980
  • 23
  • 69
  • 137

2 Answers2

1

Use the callback of setState instead, which has the updated value of this.state:

this.setState({
  query: event.target.value
}, () => alert(this.state.query));
Roy Wang
  • 11,112
  • 2
  • 21
  • 42
1

It happens because state updates are asynchronous:

React may batch multiple setState() calls into a single update for performance.

Because this.props and this.state may be updated asynchronously, you should not rely on their values for calculating the next state.

It says "this.props and this.state may be updated asynchronously" but it should almost say will be, the update is currently always asynchronous, it just may or may not be batched.

If you need to do something only after this.state has been updated, use the second argument, which is a callback for when the update has been made.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875