I encountered this problem building a webapp and I replicated it in this jsfiddle. Essentially, I would like an input to call this.setState({message: input_val})
There is a much simpler way to do this, setState(updater, callback)
is an async function and it takes the callback as second argument,
Simply pass the handleSubmit as a callback to setState method, this way after setState is complete only handleSubmit will get executed.
For eg.
handleChange: function(e) {
console.log(e.target.value);
this.setState({message: e.target.value}, this.handleSubmit);
}
Try to change the handleChange() method like above and it will work.
for syntax of using setState check this link
There's no reason for MyForm to be using state here. Also putting the onChange on the form instead of the input you're interested in is odd. Controlled components should be preferred because their behavior is more obvious, and any time App's message state changes (even if you e.g. allow Message to change it later), it'll be correct everywhere.
This also makes your code a bit shorter, and considerably simpler.
var App = React.createClass({
getInitialState: function() {
return {message: ''}
},
appHandleSubmit: function(message) {
this.setState({message: message});
},
render: function() {
return (
<div className='myApp'>
<MyForm onChange={this.appHandleSubmit}
message={this.state.message} />
<Message message={this.state.message}/>
</div>
);
}
});
var MyForm = React.createClass({
handleInputChange: function(e){
this.props.onChange(e.target.value);
},
// now always in sync with the parent's state
render: function() {
return (
<form className="reactForm">
<input type='text' onChange={this.handleInputChange}
value={this.props.message} />
</form>
);
}
});
jsbin
I found it very cumbersome for me to define 3 handler functions just to get some value to a component's state, so I decided not to use state at all. I just defined an additional property to the component that stored desired value.
So I ended up with a code that looked something like this:
//...
},
text: '',
handleChange: function(event) {
this.text = event.target.value;
this.forceUpdate();
},
render: function() {
return <div>
<InputComponent onChange={this.handleChange}/>
<DisplayComponent textToDisplay={this.text}/>
</div>
}
//...
A call to setState
isn't synchronous. It creates a "pending state transition." (See here for more details). You should explicitly pass the new input
value as part of the event being raised (like to handleSubmit
in your example).
See this example.
handleSubmit: function(txt) {
this.props.onChange(txt);
},
handleChange: function(e) {
var value = e.target.value;
this.setState({message: value});
this.handleSubmit(value);
},
I was pulling my hair out for like an hour because of this so I decided to share... If your callback is still one step behind and seemingly not working, ensure you don't CALL the function with parenthesis... Just pass it in. Rookie mistake.
RIGHT:
handleChange: function(e) {
console.log(e.target.value);
this.setState({message: e.target.value}, this.handleSubmit);
}
VS
WRONG:
handleChange: function(e) {
console.log(e.target.value);
this.setState({message: e.target.value}, this.handleSubmit());
}
with setState hook
useEffect(() => {
your code...
}, [yourState]);