Where is the code that causes setState (React) to be asynchronous? - javascript

I recently read through a large portion of the React and ReactDOM codebase in order to get an understanding of what happens when a call to setState occurs. At this point, I would say that I understand the sequence of events at a high level. A call to setState results in a message being added to a queue of updates that need to be processed. The updates are processed and changes to the DOM are only made if necessary.
Here's where I am lost. There are hundreds of blog posts discussing the asynchronous behavior of setState. While I don't doubt that setState is asynchronous, I cannot find a line of code in the ReactDOM codebase that would introduce asynchronous behavior. Does anyone know exactly where this happens?

First of all setState may be execute in async way, but it is not allwys executed as such. Ben Nadel list some of his findings in setState() State Mutation Operation May Be Synchronous In ReactJS
To summarize setStates seems to gets batched in situations where react can intercept originating event, like onClick handlers. Since react creates actual DOM from virtual react DOM (and since it is aware of semantics of attributes) it can wrap any of onClick handlers that you provide into something like this
wrappedHandler = () => {
turnBatchingOn()
originalHanlder()
executeBatched()
}
In this case you get async behavior, all setState calls get enqueued, and they get executed only after your original onClick handler has finished executing.
Note this is not actual react code it is just my speculation how it is async effect achieved. I understand it is not actual line of code that does it, but I think it could help you find it.
Best article explaining setState implementation that I found is on React Kung Fu

i think setState isn't async, but there is an optimization of multiple setState calls and in some cases sync canot be guaranteed.

Related

Why React doesn't use promise.resolve for batching updates?

Currently, React only batches updates in functions that are called by the React itself. like event handlers and lifecycles. And as far as I know, React simply calls these functions like they are wrapped in unstable_batchedUpdates.
Lets assume it's something like the following:
unstable_batchedUpdates(() => {
instance.componentDidMount();
});
So right now, we have to manually wrap functions that are called by someone other than the React inside of the unstable_batchedUpdates.
I'm also aware that in the concurrent mode, we have the updates train concept and we batch all of the state updates together.
But I have two questions,
First, how does this update train works? What mechanism decides which updates should be on the train? Is it only the time of occurrence?
And second, Why it has to be this complicated? why we couldn't do something similar to what Vue does and wrap all of the setState calls inside of a nextTick function? I know it's almost similar to what we will have in the concurrent mode, but what kind of bottlenecks and problems do we have for adding it right now, without the concurrent mode enabled?
I'm thinking of starvation problem. But I'm not sure.
Thanks in advance.

Why does wrapping a redux call into a promise allows the access of the result "immediately"

So we have a redux (with thunk middleware) / react environment.
We have the following piece of code:
onMyClick = () => {
this.props.mySynchronousActionWhichWillCreateNewReducerState();
this.setState(...my state change which wants to see the new reducer state...);
}
It will not work in this form since the code is synchronous. Which means that the react lifecycle will never get to update itself with the new props.
However if we change it like this:
onMyClick = () => {
Promise.resolve(this.props.myActionWichWillCreateNewReducerState())
.then(
() => this.setState(...my state change which want to see the new reducer state...)
);
}
It now works as "expected" (The dispatch triggers the new reducer state, the component updates, then the setState runs). First I thought this solution is error prone and works because we 'win' just a bit of time with asynchronicity to allow the update to kick in before the setState. But it never fails, (You can make the code slow in the action, or in the reducer, in the middleware, in the component update, where ever you want, it still works).
Why?
Some explanation might be warranted why this is so hard to wrap my head around. And more in the sense of "Why does it work the way it does" instead of "How does it work"
So first and foremost lets look at the two pieces of code like plain javascript.
In this case - for me at least - the first should work, and the second should not. Or at least the second should be fuzzy.
First:
I make synchronous call (dispatch->action creation->store change), then I make an other, and yet the second cannot expect the changes made by the first. I have to know how redux and react operates quite intimately to know how, and why. And btw you can even mutate the redux store (big no no) instead of returning a new object from the reducer to retain the reference and it still doesn't work. Which is mind boggling, you synchronously mutate an object, then cannot access the change afterwards...
Second:
In this case (just like Jaromanda X commented) what I "seemingly" tell the code 'Hey run these two pieces of code in parallel'. And now it works, and works all the time. Wut. Adding my (superficial or so it seems) understanding of react life cycles to the mix makes it even more paradoxical. Since it means that even more logic - react lifecycle update - will have to 'outrun' the setState call for it to work.
If this wouldn't be redux/react environment with all the support and intelligence behind it, I would say this code behavior smells like all hell and it smells like black-magic and go-to :).
When you wrap a piece of code inside a promise, you are essentially delaying its execution by a minimum of 1 tick. For your code, that time was sufficient enough for reducer dispatch to complete its update. Hence when the code inside then was executed it got the updated value, as this.state is an object and even within a closure it always points to a memory reference which will be updated.
That said neither reducer update in redux or setState in React returns a promise. Your code is equivalent to :
Promise.resolve(console.log("dummy")).then(() => console.log("second"));
console.log("first")
first will always be printed before second as the promisified snippet is executed in the next tick of the event queue.
Your code is not error prone at the moment because React decided 1 tick was sufficient to update the state. But don't rely on that as for some other piece of code or in later versions of React, the time required for updates might change.

React must call with setTimeout to update view correctly

I'm making modifications to this file https://github.com/davidguttman/react-pivot/blob/master/index.jsx#L84 to move the Dimensions component out to a parent component.
One strange thing i noticed is that i have to call setTimeout(this.updateRows, 0) instead of this.updateRows() for the views to update correctly.
Any idea why this is so? AFAIK, setTimeout(_,0) simply makes the function call asynchronous (i.e. allows concurrent execution for performance). Why would that help with rendering views correctly? I'm asking this question to avoid "Programming by Coincidence".
This is because setState is asynchronous.
Since you are reading from this.state in the updateRows function, it won't work until the state is actually updated.
Using setTimeout as you did, is one way to allow the state to update. setState will complete, and then updateRows will execute in the next frame.
A better way would be to use the callback parameter of setState
this.setState({dimensions: updatedDimensions}, () => {
this.updateRows();
});
Another option is to keep any state changes in an object and pass it into the function instead of reading directly from this.state, but this can lead to much more complexity.
In this instance it's probably less about "concurrent execution" and more about the event loop. The setTimeout call removes function execution from the current call stack and adds an entry to the message queue. The currently executing stack will run to completion before the next message in the queue begins execution.
I don't know why this is required in this particular instance - some sort of state must be getting set in the current stack that's required for updateRows to produce the desired result.

Render after componentWillReceiveProps and before componentDidUpdate

Here's my problem:
I'm new at reactjs and I'm trying to make this app using SWAPI (swapi.co). For now I need to list characters and some info about them. The problem is I have this component called SelectedCharacter that returns some info about a character that was selected in a div.
The moment a character is passed through props to this component, I get a response via xmlhttp and the info is displayed. The thing is that I want to put a "Loading..." message while the data is fetched. This is how I was trying to figure it out:
I set up the componentWillReceiveProps function, where I test if I'll need to load stuff and the componentDidUpdate, where I fetch the data from this api and update the status.
I know, from react life cycle, that a render is called between componentWillReceiveProps and componentDidUpdate, and it indeed is.
I expected, then, that if I did this:
render() {
if (criteria) {
return <div>Loading...</div>
}
}
The thing is: even if this criteria is true (I tested it using console.log()), the message doesn't show until the next re-render. Am I doing anything too wrong here? If it helps, my code is at github.com/piubellofelipe/StarWars, the problem is at the selected_characters.js, in the src paste.
Thanks
I've been looking at your code, trying to work this out for you and I don't have any concrete answers for you, but I've noticed a few things that may be making things a bit unpredictable.
1. Calling forceUpdate
componentWillReceiveProps(){
this.setState({loading:true})
this.forceUpdate();
}
Calling setState will trigger a render, so the call the forceUpdate is not required here. This means there are more renders occurring than you might expect.
I believe this may be the cause of your issue for a pretty complicated reason. From the setState docs
... setState() is also asynchronous, and multiple calls during the same cycle may be batched together.
And from the forceUpdate docs
Calling forceUpdate() will cause render() to be called on the component...
My theory is that the call render triggered by setState, asynchronously setting loading to true, is being delayed and the one from forceUpdate is sneaking in first, while loading is still false.
2. Updating props
this.props.selected.moviesList = moviesListNames;
Components should never, ever, update their own props. Usually, this would be stored in state instead.
For more details on this, read this section of the docs and this answer.
3. Importing axios, but not using it
import axios from 'axios'
This one isn't really an issue (other than an unused import), and might just be preparation for where you're heading. axios gives a much nicer developer experience than XMLHttpRequest (in my opinion) for http requests and will clean up the fetchData function a lot, which will make troubleshooting easier.
I hope this helps a bit, and you're enjoying React. Feel free to follow up on any of these points in the comments or as new questions.

How does React remove asynchronous behavior?

I have been reading this, and I came across a line in the Redux documentation
Libraries like React attempt to solve this problem in the view layer
by removing both asynchrony and direct DOM manipulation.
I understood the part where React does not allow DOM manipulations as it abstracts the actual rendering using the render() method. I am confused with the second part.
How does React remove the asynchrony? Can someone please explain with examples or valid use cases.
It removes asynchronous behavior from the view layer by design. You must not do async stuff in render or what is rendered will be broken.
Libraries like React attempt to solve this problem in the view layer
by removing both asynchrony and direct DOM manipulation [from the render method].
You are missing the last sentence which gives further explanations:
However, managing the state of your data is left up to you.
This is where Redux enters.
To wrap up, there must be no async calls in render method but there can be some async stuff happening in the state management part handled by Redux. You can't do async stuff in the render method by design. If you you do so it won't work.
I think that good example would be an rendering library like dust.js
It renders template asynchronous, creates document fragment, and let user handle appending in callback.
And well... I think that any DOM mutations performed in callbacks counts into this case.

Categories

Resources