I have the handleSelection
method called when a button is clicked, however, if I click the button once the state does not get set when it gets to this.setState({selectedFoods: newSelections});
. Everything else in the method executes correctly (as my various console.logs tell me :) ). When the button is clicked a second time, everything in the method gets executed again and the setState
works.
var Flavor = React.createClass({
getInitialState: function() {
return { foods: {}, selectedFoods: [], affinities: [] };
},
componentDidMount: function() {
$.ajax({
url: this.props.url,
dataType: 'json',
cache: false,
success: function(data) {
this.setState({foods: data});
}.bind(this),
error: function(xhr, status, err) {
console.error(this.props.url, status, err.toString());
}.bind(this)
});
},
componentDidUpdate: function () {
$('#select-food').selectize({
onChange: this.handleSelection
}
);
},
handleSelection: function (e) {
if (e.target) {
var selectedFood = e.target.id;
} else {
var selectedFood = e;
}
console.log(selectedFood);
if (this.state.foods[selectedFood]) {
var selections = this.state.selectedFoods;
var newSelections = selections.concat(selectedFood);
console.log("newSelections: "+newSelections)
var state = Object.assign(this.state, {selectedFoods: newSelections});
this.setState(state);
console.log("state: "+this.state.selectedFoods)
this.handleAffinities();
} else {
console.log("** "+selectedFood+" **");
}
},
handleAffinities: function() {
console.log("selectedFoods: "+this.state.selectedFoods.length)
if (this.state.selectedFoods.length > 0) {
var allAffinities = this.state.selectedFoods.map((food) => {
return this.state.foods[food];
});
console.log(allAffinities.length);
console.log(allAffinities);
var commonAffinities = allAffinities[0];
allAffinities.forEach((affinities) => {
commonAffinities = commonAffinities.filter((n) => {
return affinities.indexOf(n) != -1;
});
})
this.setState({affinities: commonAffinities});
} else {
this.setState({ affinities: [] });
}
},
handleRemove: function(food) {
var selectedFoods = this.state.selectedFoods;
var index = selectedFoods.indexOf(food);
var updatedSelection = selectedFoods.splice(index, 1);
this.setState({selectedFoods: selectedFoods});
this.handleAffinities();
},
Why does everything execute correctly the first time except my setState
function? And why it work on the second click?
The state is changing exactly the way it is supposed to. The problem is that your console.log
statements immediately after your call to setState
are firing before the new state is set.
From the docs:
setState()
does not immediately mutatethis.state
but creates a pending state transition. Accessingthis.state
after calling this method can potentially return the existing value.
If you'd like to fire a console.log
statement after the state transition completes, pass a function as a callback to setState()
.
this.setState({selectedFoods: newSelections}, () => {
console.log(this.state.selectedFoods);
});
As @Micheal stated, setState function stays pending, and first console.log works. Anybody who wants to see or experiment this situation, may have a look at my codesandbox.io example. Here, inside YesNoComponentWithClass, yes button logs empty for the first click, while no button logs the expected value.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With