Reputation: 1823
here is my codes:
class TestState extends Component{
constructor(props){
super(props);
this.state={
text:"123"
}
this._hello = this._hello.bind(this)
}
_hello(){
console.log("guangy will set state...");
let num = Math.floor(Math.random()*100);
this.setState({text: ""+num });
console.log("guangy after set state...");
}
componentWillUpdate(){
console.log("guangy componentWillUpdate")
}
render(){
console.log("guangy render.....")
return(<View>
<Text>{this.state.text}</Text>
<Button title="click" onPress={
()=>{
this._hello();
}
}/>
<Button title="click1" onPress={
()=>{
setTimeout(()=>{
this._hello();
}, 10);
}
}/>
</View>);
}
}
when i clicked the first button, the log is:
guangy will set state...
guangy after set state...
guangy componentWillUpdate
guangy render.....
and logs when clicked the second button:
guangy will set state...
guangy componentWillUpdate
guangy render.....
guangy after set state...
i think the render function should be called asynchronous, and actually in most situation it is, like when i clicked the first button, but when i clicked the second button, the render function seems to be called synchronous, because the "after set state" log is printed after the "render" log. why does this happen?
Upvotes: 2
Views: 921
Reputation: 31024
As per the DOCS
Think of setState() as a request rather than an immediate command to update the component. For better perceived performance, React may delay it, and then update several components in a single pass. React does not guarantee that the state changes are applied immediately. setState() does not always immediately update the component. It may batch or defer the update until later. This makes reading this.state right after calling setState() a potential pitfall.
So one would think that setState
is asynchronous. But if we look at some key words like:
React MAY delay it
setState() does not ALWAYS immediately update the component.
It MAY batch or defer the update until later.
So are we to think that setState
is may or may not be an asynchronous operation?
Well, yep.
This is setState
taken from the source code:
ReactComponent.prototype.setState = function(partialState, callback) {
invariant(
typeof partialState === 'object' ||
typeof partialState === 'function' ||
partialState == null,
'setState(...): takes an object of state variables to update or a ' +
'function which returns an object of state variables.'
);
this.updater.enqueueSetState(this, partialState);
if (callback) {
this.updater.enqueueCallback(this, callback, 'setState');
}
};
Looks like a normal synchronous function, well actually setState
as it self isn't asynchronous but the effect of its execution may be.
I've done some testing myself and i realized that react will only update the state Asynchronously is when react has control of the entire flow, where react can't control of the flow, which means the the execution context is outside of it, then react will update the state immediately.
What can take react's control then?
Things like setTimeout
, setInterval
ajax request and other webApi's.
Even an event handler that attached outside react will trigger such behavior.
In fact here is a little snippet to satisfy our experiment.
I have a React component App
which has a state with a myValue
key and a method named onClick
.
This method logs the current state, then call setstate
then logs the state again.
App
renders 3 elements:
myValue
.onClick
through the react API.onClick
through the addEventListener
API
(out side of react mind you).When we click on the first button when react has control over the flow, the state is updated asynchronously.
When we click the second button, react will update the state immediately.
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
myVal: 0
}
}
componentDidMount() {
const el = document.getElementById('btn');
el.addEventListener('click', this.onClick);
}
onClick = () => {
console.log('Pre setState', this.state.myVal);
this.setState({ myVal: this.state.myVal + 1 });
console.log('Post setState', this.state.myVal);
console.log('-------------------');
}
render() {
const { myVal } = this.state;
return (
<div>
<div>{myVal}</div>
<button onClick={this.onClick}>Managed by react</button>
<button id='btn'>Not Managed by react</button>
</div>);
}
}
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
Upvotes: 1