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})
every time I type something into it, then pass it into the parent App class which then re-renders the message onto the Message class. However the output seems to always be one step behind what I actually type. The jsfiddle demo should be self explanatory. I am wondering if I did anything wrong to prompt this.
html
<script src="http://facebook.github.io/react/js/jsfiddle-integration.js"></script>
<div id='app'></div>
js
var App = React.createClass({
getInitialState: function() {
return {message: ''}
},
appHandleSubmit: function(state) {
this.setState({message: state.message});
console.log(this.state.message);
},
render: function() {
return (
<div className='myApp'>
<MyForm onChange={this.appHandleSubmit}/>
<Message message={this.state.message}/>
</div>
);
}
});
var MyForm = React.createClass({
handleSubmit: function() {
this.props.onChange(this.state);
},
handleChange: function(e) {
console.log(e.target.value);
this.setState({message: e.target.value});
this.handleSubmit();
},
render: function() {
return (
<form className="reactForm" onChange={this.handleChange}>
<input type='text' />
</form>
);
}
});
var Message = React.createClass({
render: function() {
return (
<div className="message">
<p>{this.props.message}</p>
</div>
)
}
});
React.render(
<App />,
document.getElementById('app')
);
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);
},
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
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]);
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