First Look at the Call Stack

If you check out our call stack now, it may look a bit daunting. That’s OK. It’s still just a call stack. Some of those functions might seem foreign, but we can also find some anchors we recognize — whether it’s componentDidMount and setState , or the later functions that have to do with lifecycles.

Spoiler alert: performWorkOnRoot is where most of the magic happens — this is the place where React initiates it’s rendering phase that is described in Lin Clark’s wonderful Fiber talk. So if you really want to get your hands dirty you might consider setting a few breakpoints in that bad boy and seeing what happens.

I am so, so sorry…

We’re going to focus on following our state, and the first function that gets called after setState is enqueueSetState . So let’s see what happens there:

enqueueSetState

Its arguments are inst , payload , and callback . A quick glimpse inside setState (the function which calls it) will show us that these are respectively the component instance, the partial state passed to setState and the callback passed to setState , if there is one.

Now the first thing this function does is retrieve the component’s Fiber. If you’re unfamiliar with Fibers, check out this great in-depth explanation: https://github.com/acdlite/react-fiber-architecture, but for our purposes, let’s use this mental model — a Fiber is an internal representation of a component that React uses to do its reconciliation. All Fibers are linked together in a structure called a linked-list-tree, which is designed to allow for easy and efficient traversal between Fibers that need to share data. Here, in enqueueSetState , we can clearly see the relationship between the Fiber and the component instance.

Next, React sets the variables currentTime and expirationTime , and we’re going to take React’s word for what it does there, and not delve into the implementation details. Then it creates a variable called update with createUpdate , and saves our payload on it. The payload argument stores our partial state, so we should definitely look into that. Let’s take a peek inside our update object.

The first thing we can see here, is that the payload is in fact the partial state we had passed to setState . And we can see something else — that the expiration time for this update is not actually a time, or some big number representing a timestamp like we might expect, but 1. This is because React treats this as a synchronous update, which means it’s not going to do all that Fiber magic of stopping mid-work to yield control to the browser. I can’t tell you for sure why React does that — but my educated guess is it has to do with the fact this call came from within a lifecycle method, and React wants it to execute as soon as possible, without pausing.

We can elegantly ignore the next few lines, as they have to do with the callback parameter, which we didn’t pass a callback to our setState , I’m going to elegantly ignore those lines of code and move on. I’ve figured out that my partial state went into the update object, so now I want to follow that and see what React does with it, and the next thing it does is call enqueueUpdate .

enqueueUpdate

If we follow this function back to where it’s defined, the first thing we can see is that React reads a property called alternate off the Fiber. This function has already returned, so we could add a breakpoint here and rerun, but we still have access to our Fiber so another option is to print out the value of fiber.alternate in the console. Compare and contrast the Fiber and the Alternate for a bit and you will find this — the alternate is itself a Fiber that is a representation of the same component, with slight variations from our current Fiber. If you look more closely at the differences you might be able to spot this:

One of these states is not like the other

Keep in mind that at this point in time our component and view have already been updated, meaning that React has finished its reconciliation work and this is the final state of these two Fibers. Our takeaway from this, is one Fiber represents the previous state, and the other represents the current state.

Let’s look back to enqueueUpdate for a minute. What else does it do? Well, it seems to do some checks on the two Fibers, to see which of them exists, and also tests a specific property on the Fibers, the updateQueue :