Peter
Peter

Reputation: 1904

Component won't rerender after setState

I recently got started with React and want to build a little application to fetch weather data. My API has a function to return autocomplete suggestions. So when my autosuggestion array is not empty I render a list and upon clicking one of the <li>'s I want the value inside of the input box. I manage to set the state of my SearchBar but can't change it's value.

Edit: I try to get my value from changeState() into my <input type="text" placeholder="City, Zip Code, Coordinates" onChange={evt => this.updateInputValue(evt)} />. I can search for terms otherwise.

import React from 'react';
import './SearchBar.css';
import Suggestion from './Suggestion';

class SearchBar extends React.Component{
  constructor(props) {
    super(props);
    this.state = {inputValue: ''};
    this.search = this.search.bind(this);
    this.updateInputValue = this.updateInputValue.bind(this);
    this.handleKeyPress = this.handleKeyPress.bind(this);
    this.changeState = this.changeState.bind(this);
  }

  changeState(value) {
    console.log(value);
    // Logs value of text between <li></li>
    this.setState({inputValue: value});
  }

  search() {
    this.props.onSearch(this.state.inputValue);
  }

  updateInputValue(evt) {
    this.setState({
      inputValue: evt.target.value
    });
    this.props.onChange(this.state.inputValue);
  }

  handleKeyPress(e) {
    if(e.key === 'Enter') {
      this.search();
    }
  }

  render() {
    return (
      <div>
        <div className="SearchGroup" onKeyPress={this.handleKeyPress} >
          <input type="text" placeholder="City, Zip Code, Coordinates" onChange={evt => this.updateInputValue(evt)} />
          <a onClick={this.search}>Go</a>
        </div>
        <Suggestion autocomplete={this.props.autocomplete} onSelect={this.changeState} />
      </div>
    );
  }
}

export default SearchBar;

For the sake of completeness my Suggestion.js:

import React from 'react';
import './Suggestion.css';

class Suggestion extends React.Component{
  constructor(props) {
    super(props);
    this.updateInputField = this.updateInputField.bind(this);
  }

  updateInputField(evt) {
    this.props.onSelect(evt.currentTarget.innerText);
  }

  render(){
      if(this.props.autocomplete && this.props.autocomplete.length > 0) {
        return (
          <div className="Suggestion">
            <ul>
              {
                this.props.autocomplete.map((location) => {
                  return (
                    <li key={location.id} onClick={this.updateInputField}>{location.name}</li>
                  )
                })
              }
            </ul>
          </div>
        );
      } else {
        return <div className="None"></div>
      }
  }
}

export default Suggestion;

I would also prefer to submit location.url in Suggestion, but I could not find a property that matches inside of evt.

Upvotes: 0

Views: 5095

Answers (3)

Qais Palekar
Qais Palekar

Reputation: 101

The React setState doesn't update the state immediately. It puts it in the queue and updates the state in batches. if you want to access the updated state write the code in the setState callBack

this.setState({ inputValue: evt.target.value},()=> this.props.onChange(this.state.inputValue));

something like this

Upvotes: 0

Luk&#225;š Gibo Vaic
Luk&#225;š Gibo Vaic

Reputation: 4420

I would guess that you are trying to use value from state that isnt there yet, because setState is asynchronous so either use callback on setState

updateInputValue(evt) {
  this.setState({
    inputValue: evt.target.value
  }, ()=> this.props.onChange(this.state.inputValue));
}

or, use the value from event directly

updateInputValue(evt) {
  const value = evt.target.value
  this.setState({
    inputValue: value
  });
  this.props.onChange(value)
}

plus you havent assigned value back to your input:

<input type="text" placeholder="City, Zip Code, Coordinates" onChange={evt => this.updateInputValue(evt)} value={this.state.inputValue}/>

Upvotes: 0

Hemadri Dasari
Hemadri Dasari

Reputation: 34014

As mentioned in my comment. You are setting state and immediately passing state to onChange function in updateInputValue event handler function which is not correct. Because you won't get the state value updated immediately, the state value updates only when it renders so, pass evt.target.value directly like below

updateInputValue(evt) { 
   this.setState({ inputValue: evt.target.value }); 
   this.props.onChange(evt.target.value);
}

In order to see chnaged value on your input field, you have to pass value prop to input tag like below

<input type="text" placeholder="City, Zip Code, Coordinates" onChange={evt => this.updateInputValue(evt)} value={this.state.inputValue}/>

Upvotes: 1

Related Questions