In-depth explanation of state and props update in React

A glimpse into the internals of React Fiber

Max Koretskyi
Dec 11, 2018 · 13 min read

ReactInDepth is moving away from Medium. This article, its updates and more recent articles are hosted on the new platform

In my previous article Inside Fiber: in-depth overview of the new reconciliation algorithm in React I laid the foundation required to understand the technical details of the update process that I’ll describe in this article.

I’ve outlined main data structure and concepts that I’ll be using in this article, particularly Fiber nodes, current and work-in-progress trees, side-effects and the effects list. I’ve also provided a high-level overview of the main algorithm and explained the difference between the and phases. If you haven’t read it, I recommend that you start there.

I’ve also introduced you to the sample application with a button that simply increments a number rendered on the screen:

You can play with it here. It’s implemented as a simple component that returns two child elements and from the method. As you click on the button, the state of the component is updated inside the handler. This results in the text update for the element:

Here I’ve also added the lifecycle method to the component. This is needed to demonstrate how React adds effects to call this method during the phase.

In this article I want to show you how React processes state updates and builds the effects list. We’ll take a tour into what’s going on in the high-level functions for the and phases.

Particularly, we’ll see how that in React:

  • updates the property in the of
  • calls the method to get a list of children and performs comparison
  • updates the props for the element

And, in React:

  • updates the property of the element
  • calls the lifecycle method

But before that, let’s quickly take a look at how the work is scheduled when we call in a click handler.

Note that you don’t need to know any of it to use React. This article is about how React works internally.

I work as a Developer Advocate at ag-Grid. If you’re curious to learn about data grids or looking for the ultimate React data grid solution, get in touch or give it a try with the guide “Get started with React grid in 5 minutes”. I’m happy to answer any questions you may have. Follow me to stay tuned!

Scheduling updates

When we click on the button, the event is triggered and React executes the callback that we pass in the button props. In our application it simply increments the counter and updates the state:

Every React component has an associated which acts as a bridge between the components and the React core. This allows to be implemented differently by ReactDOM, React Native, server side rendering, and testing utilities.

In this article we’ll be looking at the implementation of the updater object in ReactDOM, which uses the Fiber reconciler. For the component it’s a . It’s responsible for retrieving an instance of Fiber, queuing updates, and scheduling the work.

When updates are queued, they are basically just added to the queue of updates to process on a Fiber node. In our case, the Fiber node corresponding to the component will have the following structure:

As you can see, the function in the is the callback we passed to in the component. It represents the first update that needs to be processed during the phase.

Processing updates for the ClickCounter Fiber node

The chapter on the work loop in my previous article explains the role of the global variable. Particularly, it states that this variable holds a reference to the Fiber node from the tree that has some work to do. As React traverses the tree of Fibers, it uses this variable to know if there’s any other Fiber node with unfinished work.

Let’s start with the assumption that the method has been called. React adds the callback from to the on the fiber node and and schedules work. React enters the phase. It starts traversing from the topmost Fiber node using the renderRoot function. However, it bails out of (skips) the already processed Fiber nodes until it finds a node with unfinished work. At this point there’s only one Fiber node with some work to do. It’s the Fiber node.

All work is performed on the cloned copy of this Fiber node is stored in the field. If the alternate node is not yet created, React creates the copy in the function createWorkInProgress before processing updates. Let’s assume that the variable holds a reference to the alternate Fiber node.


First, our Fiber gets into the beginWork function.

Since this function is executed for every Fiber node in a tree it’s a good place to put a breakpoint if you want to debug the phase. I do that often and check the type of a Fiber node to pin down the one I need.

The function is basically a big statement that determines the type of work that needs to be done for a Fiber node by the tag and then executes the respective function to perform the work. In the case of it’s a class component, so this branch will be taken:

and we get into the function. Depending on whether it’s the first rendering of a component, work being resumed, or a React update, React either creates an instance and mounts the component or just updates it:

Processing updates for the ClickCounter Fiber

We already have an instance of the component, so we get into the . That’s where React performs most of the work for class components. Here are the most important operations performed in the function in the order of execution:

  • call hook (deprecated)
  • process updates in the and generate new state
  • call with this new state and get the result
  • call the to ensure a component wants to update;
    if , skip the whole rendering process, including calling on this component and its children; otherwise proceed with the update
  • call (deprecated)
  • add an effect to trigger lifecycle hook

Although the effect to call is added in the phase, the method will be executed in the following phase.

  • update and on the component instance

and should be updated on the component instance before the method is called, since the method output usually depends on the and . If we don’t do that, it will be returning the same output every time.

Here’s the simplified version of the function:

I’ve removed some auxiliary code in the snippet above. For instance, before calling lifecycle methods or adding effects to trigger them, React checks if a component implements the method using the operator. Here is, for example, how React checks for the method before adding the effect:

Okay, so now we know what operations are performed for the Fiber node during the render phase. Let’s now see how these operations change values on the Fiber nodes. When React begins work, the Fiber node for the component looks like this:

After the work is completed, we end up with a Fiber node that looks like this:

Take a moment to observe the differences in properties values.

After the update is applied, the value of the property is changed to in the and the in . React has also updated the state in the component instance.

At this point, we no longer have updates in the queue, so is . And importantly, we have changes in the property. It’s no longer , it’s value is . In binary this is , which means that the third bit is set, which is exactly the bit for the side-effect tag:

export const Update = 0b00000000100;

So to conclude, when working on the parent Fiber node, React calls the pre-mutation lifecycle methods, updates the state and defines relevant side-effects.

Reconciling children for the ClickCounter Fiber

Once that’s done, React gets into the finishClassComponent. This is where React calls the method on a component instance and applies its diffing algorithm to the children returned by the component. The high-level overview is described in the docs. Here’s the relevant part:

When comparing two React DOM elements of the same type, React looks at the attributes of both, keeps the same underlying DOM node, and only updates the changed attributes.

If we dig deeper, however, we can learn that it actually compares Fiber nodes with React elements. But I won’t go into much details now as the process is quite elaborate. I’ll write a separate piece that focuses particular on the process of child reconciliation.

If you’re anxious to learn details on your own, check out the reconcileChildrenArray function since in our application the method returns an array of React Elements.

At this point there are two things that are important to understand. First, as React goes through the child reconciliation process, it creates or updates Fiber nodes for the child React elements returned from the method. The function returns the reference to the first child of the current Fiber node. It will be assigned to the and processed later in the work loop. Second, React updates the props on the children as part of work performed for the parent. To do that it uses data from the React elements returned from method.

For example, here’s what the Fiber node corresponding to the element looks like before React reconciles the children for the fiber:

As you can see, the property in both and is . Here’s the structure of the React element returned from the for the element:

As you can see, there’s a difference between the props in the Fiber node and the returned React element. Inside the function that is used to create alternate Fiber nodes, React will copy the updated properties from the React element to the Fiber node.

So, after React has finished reconciling the children for the component, the Fiber node will have the updated. They will match the value in the React element:

Later, when React will be performing work for the Fiber node, it will copy them to the and add effects to update DOM.

Well, that’s all the work that React performs for the fiber node during the render phase. Since the button is the first child of the component, it will be assigned to the variable. There’s nothing to be done with it, so React will move to its sibling, which is Fiber node. According to the algorithm described here, it happens in the function.

Processing updates for the Span fiber

So, the variable now points to the alternate of the fiber and React starts working on it. Similar to the steps performed for the , we start with the beginWork function.

Since our node is of type, this time in the switch statement React takes this branch:

and ends up in the function. You can see a parallel with the function called for class components. For a functional component it’ll be and so on. You can find all these functions in the file.

Reconciling children for the span fiber

In our case there nothing important happening for the node in the .

Completing work for the Span Fiber node

Once is finished, the node gets into the function. But before that, React needs to update the on the span fiber. You may remember that when reconciling children for the component, React updated the on the Fiber node:

So once is finished for the fiber, React updates to match :

It then calls the function which is basically a big statement similar to the one we saw in :

Since our Fiber node is , it runs the function. In this function React basically does the following:

  • prepares the DOM updates
  • adds them to of the fiber
  • adds the effect to update the DOM

Before these operations are performed, the Fiber node looks like this:

and when the work is completed it looks like this:

Notice the difference in the and fields. It’s no longer , it’s value is . In binary this is , which means that the third bit is set, which is exactly the bit for the side-effect tag. That’s the only job React needs to do for this node during the following commit phase. The field holds the payload that will be used for the update.

Once React has processed and its children, it’s done with the phase. It can now assign the completed alternate tree to the property on . This is the new tree that needs to be flushed to the screen. It can be processed immediately after the phase or picked up later when React is given time by the browser.

Effects list

In our case, since the node and the component have side effects, React will add a link to the Fiber node to the property of .

React builds the effects list in the function. Here’s what a Fiber tree with effects to update text of the node and calls hooks on looks like:

And here’s the linear list of nodes with effects:

Commit phase

This phase begins with the function completeRoot. Before it gets to do any work, it sets the property on the to :

root.finishedWork = null;

Unlike the first phase, the phase is always synchronous so it can safely update to indicate that the commit work has started.

The phase is where React updates the DOM and calls the post mutation lifecycle method . To do that, it goes over the list of effects it constructed during the previous phase and applies them.

We have the following effects defined in the phase for our and nodes:

The value of the effect tag for is or in binary and defines the work which basically translates into the lifecycle method for class components. The least significant bit is also set to signal that all work has been completed for this Fiber node in the phase.

The value of the effect tag for is or in binary and defines the work for the host component DOM update. In the case of the element, React will need to update for the element.

Applying effects

Let’s see how React applies those effects. The function , which is used to apply the effects, consists of 3 sub-functions:

Each of those sub-functions implements a loop that iterates over the list of effects and checks the types of the effects. When it finds the effect pertaining to the function’s purpose, it applies it. In our case, it will call the lifecycle method on the component and update the text of the element.

The first function commitBeforeMutationLifeCycles looks for the effect and calls the method. But, since we didn’t implement the method on the component, React didn’t add the effect during the stage. So in our case, this function does nothing.

DOM updates

Next React moves to the function. This is where React will change the text on the element from to . There’s nothing to do for the fiber because nodes corresponding to class components don’t have any DOM updates.

The gist of the function is that it selects the correct type of effect and applies the corresponding operations. In our case we need to update the text on the element, so we take the branch here:

By going down to , we will eventually get into the function. It takes the payload that was added during the stage to the Fiber node, and updates the property on the element:

After the DOM updates have been applied, React assigns the tree to the . It sets an alternate tree as current:

root.current = finishedWork;

Calling post mutation lifecycle hooks

The last remaining function is . This where React calls the post mutational lifecycle methods. During the phase, React added the effect to the component. This is one of the effects that the function looks for and calls method:

The function also updates refs, but since we don’t have any this functionality won’t be used. The method is called in the function:

You can also see that this is the function where React calls the method for components that have been rendered for the first time.

And that’s it!

We’re finally done. Let me know what you think about the article or ask questions in the comments. I have many more articles in the works providing in-depth explanation for scheduler, children reconciliation process and how effects list is built. I also have plans to create a video where I’ll show how to debug the application using this article as a basis.

For more insights follow me on Twitter and on Medium. Thanks for reading! If you liked this article, hit that clap button below 👏. It means a lot to me and it helps other people see the story.

React Grid — the fastest and most feature-rich grid component from ag-Grid

React In Depth

The place where advanced React concepts are explained

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store