React Internals — JSX transformation to JS and DOM
When I moved to React, for the initial few months I felt why we are writing something like JSX, why can’t we just write HTML instead? Kept wondering that JSX is just mimicking the HTML(I used to work on Angular and AgularJS). Then I started reading about JSX and how it’s not [just] an HTML copycat.
What problems do JSX tries solve?
When we update anything in DOM, it a costly operation. When something is updated, that something may go through a sequence of events and different phases, this takes time and resources.
So, now how does JSX differs from this? Isn’t it updating DOM? The answer is yes, but how it does makes a difference. A JSX goes through a sequence before creating/updating DOM. Many times we hear about React uses something know as Virtual DOM.
How does React solve this problem?
So, does react creates a copy of Physical DOM(what uses sees) and call it Virtual DOM and updates it? The answer is yes and no both. No, because the DOM that the user sees on screen is HTML/XML-like structure, and React doesn’t keep that in form of HTML/XML-like, and Yes because it’s a JS object(React.Element).
Updating objects are much faster than updating DOM with every create
→ update
→ unload
phase. As developers, we write JSX and React converts to JSX.Element and with the process know as Reconciliation(a different topic itself) and it applies changes to Physical DOM.
What are the steps?
So, let’s understand this by the below example, using typescript just to show actual types.
First, this JSX will go to React.createElement()
API. React.createElement API takes three arguments, they are type
, props
, children
. Considering the above example it will be something as follow
- The first is
type
, it could be a string or reference to a functional/class component orSymbol(react.fragment)
when usingReact.Fragment
. If it’s an existing HTML tag(i.e. div, span) it’ll be a lowercase string. If it’s a custom component it must start with a capital letter, many times we get an error when we name our component starting with a lowercase character. - The second one
props
, it could be an object/null. Any props(custom or inbuilt i.e.ref
,children
,key
) that we provide on a component go here. - The third one is
children
, it’s an optional parameter. This can be a string, another createElement(when using nesting) instance.
Following is an example of a nested component
One last example before we move to JSX.Element, with our own custom component
Representing createElement() in terms of object
So, let’s go back to the initial example
So, finally, React.createElement()
returns JSX.Element
that looks like an object. Whenever something changes, React tries to compare the previous object with the current and decides what to keep, discard and re-create. Let’s try to understand this JSX.Element
$$typeof
: this acts as a unique identifier for a subset. Symbols are unique always.type
: Similar to what we had in createElement()’s type argument.key
: key prop is required when iterating over the list/array.ref
: Reference to actual DOM node.props
: Similar to createElement()’s props argument, here you’ll notice there is no children property on JSX.Element object, instead it is inside JSX.Element.props- There are some other properties which
_owner
,_self
,_source
,_store
Let’s also see what happens when there are nested/child components
And finally, everything is rendered using the ReactDOM.render()
method. Playground for JSX to createElement