damtypo damtypo - 3 months ago 10
Javascript Question

React: component only rendering new values on second onClick of button

I making a react app and I have a parent component

Search
with child components
Input
and
Result
.
Input
has a drop down menu which passes a value,
genreValue
, to
Search
, through a callback function when a button is clicked. Search then makes an api call, which works fine.

My problem is it takes two clicks of the button for the new API data to render. Looking at other SO questions I suspect I need to pass
genreValue
as an argument to the cb function, or my onClick is only initialising, rather than invoking it on the first click.

It's a pretty simple app so I wouldn't think Flux etc would be needed. My console logs seem to show the value being changed in the
Search
and
Input
components.
So what am I doing wrong?

Search.js

let Search = React.createClass ({
getInitialState(){
return {
movies: ['Men In Black'],
genreValue: '12'
};
},

componentDidMount(){
this.getMovies()
},

getMovies(){
let genre = this.state.genreValue;
let url = `http://api.themoviedb.org/3/discover/movie?${key}&with_genres=${genre}`;
Request.get(url).then((response) => {
console.log('response.body.results', response.body.results)
this.setState({
movies: response.body.results.map(function(movie){
return movie.title
})
});
});
},

handleGenre(newGenre) {
this.setState({ genreValue: newGenre })
return this.getMovies();
},

render(){
console.log(this.state.movies)
console.log('genreValue state', this.state.genreValue)
return (
<div>
<Input genre={this.state.genreValue} onGenreChanged={this.handleGenre}/>
<ul>
{this.state.movies.map( function(movie){
return <Results key={movie.id} data={movie}/>;
})}
</ul>
</div>
);
}

});

export default Search;


Input.js

let Input = React.createClass ({

selectHandler(){
return this.props.onGenreChanged(this.refs.genre.value);
},

render() {
console.log('genreValue prop', this.props.genre);
console.log('refs', this.refs.genre)
return <div>
<select ref="genre">
<option value="28">Action</option>
<option value="12">Adventure</option>
<option value="16">Animation</option>
<option value="35">Comedy</option>
<option value="80">Crime</option>
<option value="99">Documentary</option>
<option value="18">Drama</option>
<option value="10751">Family</option>
<option value="14">Fantasy</option>
<option value="10769">Non-english</option>
<option value="36">History</option>
</select>

<button onClick={this.selectHandler} value="Go">Go</button>
</div>
}
});

export default Input;

Answer

In the handleGenre function, state may not have updated when this.getMovies is called. You could change it to the following:

handleGenre(newGenre) { this.setState({ genreValue: newGenre }, function() { return this.getMovies(); }); },

Or, probably better practice would be to call this.getMovies in a componentDidUpdate lifecycle function if genreValue has changed:

componentDidUpdate: function(prevProps, prevState) { if (prevState.genreValue !== this.state.genreValue) { this.getMovies(); } }