You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This learning starts from inspiring series of blog posts Didact: DIY your own React, which has been updated in his new blog post with Fiber and Hooks implementations (drastically simplified but core concept remains true).
APIs necessary to define components. like React.createElement() and to define and update states
Reconcilers
This manages to generate next snapshot of UI (represented by element object tree) based on latest state, also be able to do diff and figure out the minimal updates (platform calls) the Renderer needs to take. It is more concerned with 'WHAT to render on screen'
Renderers
Renders manage how a React tree turns into underlying platform calls
For example ReactDOM turns it to imperative, mutative calls to DOM API (appendChild, createTextNode..), ReactNative turns it into a single JSON message that lists mutations [['createView', attrs], ['manageChildren',] ...].
With this kind of separation, It allows different renderers to handle platform specific while reusing the same React core and reconciling algorithms. Renderers is mainly to encapsulate the 'HOW' part.
My learning and experimental code repo are focused on Reconciler algorithm and a bit of React core, to able to return the element object from the JSX
React Element
React.Element is light weight object representation of actual UI (e.g DOM in web)
Component is the definition to return the Element. It can compose other components using HTML like syntax (JSX) to create complext UI structure.
Let's say we have a list of stories (or any type of items), we can 'Like' each story and the number goes up. The component might look like this:
Before actual running, Babel plugin will recursively check the JSX and transpile each node to createElement call. For , it will be like:
createElement(// type'li',// props, will be null if no props{className: 'row'},// children as the rest of parameters// button is the element which only contains text elements (leaf)createElement('button',{onClick: onToggleLike},likes,'\u2764\uFE0F'),createElement('a',{href: url},name));
The returned element object tree representing <StoryLike> would be:
This is the reconciling algorithm before React 16. This reconciler uses recursion to walk through the element object tree to build the internal instances hierarchy. As recursion cannot be interrupted once it's started, it could block browser UI thread and user interaction suffers when it takes long time, which is common for complex UI (e.g to render long list with complex data)
Consider we have an App which renders only one StoryLike component
When render(<App title='Stack Reconciler' />), it recursively builds the internal instance hierarchy corresponding to each level in the elemement object tree. There are two main types of instances for two types of element, one for primitive whose type is string (e.g div, li), one for custom component which has type 'function'
CompositeComponent
It is the instance wrapper for custom component (element.type is function), it mainly runs the the function body or render menthod to keep 'unwrapping' the element object defined in it
DOMComponent
It is the instance wrapper for primitive elements (type is string, h1, li etc). It mainly maintains the ref to DOM node, a list of children which could be other Composite/DOM internal instances
The internal instance hierarchy can be represented as below:
We could also call it incremental reconciler. It still needs to traverse the element object tree, just the process can be split into chunks and spread it out over multiple call stack frames. Compared to Stack Reconciler, it does not rely on recursion that has to be finished in one single call stack, avoid blocking UI thread.
In this implementation, it demonstrates a simple scheduling to split traversal via requestIdleCallback. It's more like building a linked list incrementally. The element object tree is transformed to fiber nodes linked together in parent → first child → sibling and back to parent fashion.
The same render(<App title='Fiber Reconciler' />) above, its reconciliation process can be visualised as below:
From 1 to 17, it can be interrupted at any time based on the priorities or time is up for browser to draw current frame in the UI thread every 16.6ms (60fps frames per second, means 1000ms/60 = 16.6ms per frame)
The final commit phase is to actually do DOM operation (place new nodes, update or deletion), which need to be done in one go, so user can see full content / style painted at once.
The text was updated successfully, but these errors were encountered:
6thfdwp
changed the title
React Reconciliation
Comparison of two React Reconciliations - Simplified
Apr 20, 2021
As it stated React Fiber reconciliation makes many features possible like Suspense (optimising IO bound, avoid unnecessary loading state spinning around) and Concurrent Mode (optimising CPU bound ops with interruptible rendering with sophisticated scheduling, can be paused, resumed or aborted)
These features should be coming in React 18 release. It's clear that React is pushing the boundary of performant UI runtime in a single call stack (thread)
This learning starts from inspiring series of blog posts Didact: DIY your own React, which has been updated in his new blog post with Fiber and Hooks implementations (drastically simplified but core concept remains true).
Core Concepts Overview
The React contains 3 main packages
React Core
APIs necessary to define components. like
React.createElement()
and to define and update statesReconcilers
This manages to generate next snapshot of UI (represented by element object tree) based on latest state, also be able to do diff and figure out the minimal updates (platform calls) the Renderer needs to take. It is more concerned with 'WHAT to render on screen'
Renderers
With this kind of separation, It allows different renderers to handle platform specific while reusing the same React core and reconciling algorithms. Renderers is mainly to encapsulate the 'HOW' part.
My learning and experimental code repo are focused on Reconciler algorithm and a bit of React core, to able to return the element object from the JSX
React Element
React.Element is light weight object representation of actual UI (e.g DOM in web)
Component is the definition to return the Element. It can compose other components using HTML like syntax (JSX) to create complext UI structure.
Let's say we have a list of stories (or any type of items), we can 'Like' each story and the number goes up. The component might look like this:
Before actual running, Babel plugin will recursively check the JSX and transpile each node to
createElement
call. For , it will be like:The returned element object tree representing
<StoryLike>
would be:So the first thing we need is to implement the simplified version of
React.createElement
, the function signature would be:Stack Reconciler
This is the reconciling algorithm before React 16. This reconciler uses recursion to walk through the element object tree to build the internal instances hierarchy. As recursion cannot be interrupted once it's started, it could block browser UI thread and user interaction suffers when it takes long time, which is common for complex UI (e.g to render long list with complex data)
Consider we have an
App
which renders only one StoryLike componentWhen
render(<App title='Stack Reconciler' />)
, it recursively builds the internal instance hierarchy corresponding to each level in the elemement object tree. There are two main types of instances for two types of element, one for primitive whose type is string (e.g div, li), one for custom component which has type 'function'It is the instance wrapper for custom component (element.type is function), it mainly runs the the function body or
render
menthod to keep 'unwrapping' the element object defined in itIt is the instance wrapper for primitive elements (type is
string
, h1, li etc). It mainly maintains the ref to DOM node, a list of children which could be other Composite/DOM internal instancesThe internal instance hierarchy can be represented as below:
Fiber reconciler
We could also call it incremental reconciler. It still needs to traverse the element object tree, just the process can be split into chunks and spread it out over multiple call stack frames. Compared to Stack Reconciler, it does not rely on recursion that has to be finished in one single call stack, avoid blocking UI thread.
In this implementation, it demonstrates a simple scheduling to split traversal via
requestIdleCallback
. It's more like building a linked list incrementally. The element object tree is transformed to fiber nodes linked together in parent → first child → sibling and back to parent fashion.The same
render(<App title='Fiber Reconciler' />)
above, its reconciliation process can be visualised as below:From 1 to 17, it can be interrupted at any time based on the priorities or time is up for browser to draw current frame in the UI thread every 16.6ms (60fps frames per second, means 1000ms/60 = 16.6ms per frame)
The final commit phase is to actually do DOM operation (place new nodes, update or deletion), which need to be done in one go, so user can see full content / style painted at once.
The text was updated successfully, but these errors were encountered: