Deep Dive into react fiber and render process

Deep Dive into react fiber and render process

In this chapter, I will try to cover on how react works internally, we all have seen the high level diagrams and definitions of virtual dom, state rendering etc. But can you create your own react like library ?

This article will help you to actually understand that what happens under the hood

No alt text provided for this image


So as per above screenshot,

  1. I am creating a class components, with state object,
  2. it has a render method with a jsx template containing only an input which increases the counter on key Up

As you can see I am extending it with 'Component' class from react. Component class is imported from react module.

Inside /root/packages/react/src/React.js , you can see React module is exporting the component, PureComponent , createContext etc.

No alt text provided for this image

Component Class

If you open the file /root/packages/react/src/ReactBaseClass.js, here you will find the definition of Component class.

No alt text provided for this image

Component class has three parameters,

  1. props - Input Properties
  2. context - React Context
  3. Updater Object - Here all the things happenes

Rest of the methods are assigned to prototype of Component function in the same file.

No alt text provided for this image

You might be most familiar with setState method. as you can see it takes two inputs:

  1. PartialState: State values to be updated
  2. callback function

Both these parameters are later passed to of updater's enqueueSetState method

this.updater.enqueueSetState(this, partialState, callback, 'setState');

But from where does this updater object is prepared and passed to your component function. Where is the enqueueSetState function is defined? Let's try to find the answers inside the codebase of react library.

Updater Object and EnqueueSetState Method

There is a long sequences of processing in react before updater object is attached to a Component.

So we need to check how updater object is prepared behind the curtains.

An root react element should be initialized using 'ReactDOM.render' method

No alt text provided for this image

React's magic starts from render method. So React will render the root component and reconciliation of it's children components as well.

Rendering Root Component

Following Steps are used to render the root.

Preparing Root Fiber.

(/root/packages/react-dom/src/client/ReactDOM.js). Root Fiber is of type 'Fiber' , definition of Fiber is declared in /root/packages/react-reconciler/ReactFiber.js. There are many attributes of a 'Fiber' Object, but major attributes to remember are

a. tag: tag is type of WorkTag. This is to determine whether Fiber needs to be created on ClassComponent or HostComponent or Functional Component. There are 21 types of WorkTag as per code (/packages/shared/ReactWorkTags.js)

No alt text provided for this image

b. stateNode: It is the internal state of the particular Fiber.

There are several other important attributes of a 'Fiber' class like key, expiration time etc, refer the file /root/packages/react-reconciler/ReactFiber.js

So inside ReactDOM.js - >

No alt text provided for this image

So once the root fiber is created , it is assigned to the container component i.e. <App/>

as marked above FiberRoot is different from 'Fiber' as it has few extra properties, most property of our concern is 'current' , which marks root fiber as current.

Next Step is to render complete tree of root component

Render sub tree

Once the root fiber is attached to container component, it calls updateContainer internally. (/root/packages/react-dom/src/client/ReactDOM.js

updateContainer(element, root, parentComponent, work._onCommit);

here root is -> Fiber Root

element-> <App/> - React Node

parentComponent -> null (Since App is our root component)

We have reached till render Root, means we are about to render our root Component (App) in thhe DOM.

No alt text provided for this image

As you can see in the diagram we have created a parallel queue over root fiber.

renderRoot will create another loop, in which it will first mount the root component, and then loop on subtree component and reconcile them. During reconciliation of each child component, it will attach a fiber to that, which will be responsible to enqueue the updates.

Please keep in mind that our 'current' fiber is root, once we try to mount other components, we need to change the reference of 'current' to the corresponding fiber.

No alt text provided for this image

So we have reached till 'beginWork' method inside ReactFiberBeginWork.js. so we are now going to mount our root component. To recall out App Component has a render function, props, and few lifecycle methods. So beginWork will try to mount the Component Class with these parameters.

'workInProgress' is the fiber on which are working, keep in mind this fiber is not the 'current'. workInProgress.tag is a type of WorkTag, it can be a HostComponent, HostRoot, ClassComponent, Function Component, Lazy Component etc, there is a switch case logic inside beginWork method, which mount the component as per tag type.

it is our 'HostRoot' which we are mounting for the first itme.

so workInProgress.tag = 3 //HostRoot

No alt text provided for this image

(/root/packages/react-reconciler/src/ReactFiberBeginWork.js)

No alt text provided for this image

I have extended previous diagram a little bit more, your main focus should be on 'workLoop', which is a while loop. So this workLoop calls beginWork method each time and gets a new workInProgress Fiber each time, untill there are no children left.

So In our case once we have rendered our root, our App component will reconcile , and gets a new workInProgress Fiber, Since App Component is class component, our beginWork method will work differently.

No alt text provided for this image

You can see in the image, beginWork calls updateClassComponent internally. and our current is marked on root fiber

No alt text provided for this image


Since we are rendering the component for the first time , we have created an instance and passed to adoptClassInstance method, which will add 'updater' (Check first few paragraphs of article) to our class component.

No alt text provided for this image

If we check classComponentUpdater

No alt text provided for this image

So here is our enqueueSetState, which we were finding from start.

One final topic is remaining is how and when the lifecycle methods are called

No alt text provided for this image

As you can see after constructCallInstance, mountClassInstance Method is called, when we are rendering for first time. which internally calls componentWillMount, on created component instance

IF we are re-rendering instance, updateClassInstance method is called, which internally class, componentWillReceiveProps,

Once the either mountClassInstance or updateClassInstance are called, finishClassComponent(/packages/react-reconciler/src/ReactFiberBeginWork.js) is called to complete the rendering of component . it calls the render method on class instance 'instance.render()'. which compiles jsx internally.

finishClassComponent also calls componentDidMount or shouldComponentUpdate Lifecycle methods if they are sitting on the fiber.

If given class component has children, then it will run reconciliation on them, and update the 'workInProgress' fiber again to call 'beginWork' method.

This process will be running until all components in the render tree gets reconciled till no children left.

It is a long read, but definitely worth your time, as it helps to know react better, there are many other react methods, which we have not covered like rendering of react-native, rendering of function components etc. But once you understand the flow, rest will be also easier to understand.

要查看或添加评论,请登录

社区洞察

其他会员也浏览了