Rendering in React is one of the key concepts that often gets overlooked and there seems to be a lot of misunderstanding about it. I will cover the basic ideas about the process of rendering here.
What is Rendering?
Rendering is the process of React gathering information from the components on how they want to be displayed in the UI. This is based on the current state and props.
During the rendering, React will start from the root of the component tree and then traverse downwards to find all components that have been flagged as needing updates. For each flagged component, React will call either render()
method (for class components) or FunctionComponent()
(for function components), and save the render output. Render output is normally written in JSX syntax, which is just syntactic sugar for calling React.createElement
. This method returns React elements, which are plain JS objects that describe the intended structure of the UI.
After React has collected the render output from the entire component tree, it will diff the new tree of objects (also known as the "virtual DOM"), and collect a list of all the changes that need to be applied to the real DOM. This process is known as "reconciliation". React then applies all the calculated changes to the DOM in one synchronous sequence.
This work can be divided in two phases:
- Render phase: contains all the work of rendering components and calculating changes
- Commit phase: the process of applying those changes to the DOM
After the commit phase, React updates all refs accordingly to point to the requested DOM nodes and component instances. It then synchronously runs the componentDidMount
(after the initial render) or componentDidUpdate
(after the re-renders) class lifecycle methods, and the useLayoutEffect
hooks. React then sets a short timeout, and when it expires, runs all the useEffect
hooks.
An important thing to understand is that rendering is not the same as updating the DOM. A component may be rendered without any visible changes happening as a result.
Element vs Component
As already mentioned, elements are plain JS objects that represent a virtual DOM node. Creating a React element is cheap. Once an element is created, it is never mutated.
Components return elements as their output. A component can be declared in several different ways. It can be a class with a render()
method or it can be a function. In either case, it takes props as an input and returns an element tree as the output.
Rendering vs Mounting
Initial rendering happens before mounting and it returns the elements that are supposed to be mounted in the DOM. Mounting can be described as the actual addition of the DOM elements into the browser DOM for the first time.
Mounting happens once, while rendering can happen any number of times.
Re-rendering
After the initial render has been completed, there are a few different ways to tell React to queue a re-render.
Class components:
this.setState()
this.forceUpdate()
Function components:
useState
settersuseReducer
dispatches
Other:
- Calling
ReactDOM.render(<App>)
again (which is equivalent to callingforceUpdate()
on the root component)
Rendering a parent component will cause all of its child components to be rendered too(whether props of the component have changed or not).
Re-rendering vs Re-mounting
If the same component type is used in the same place in the tree, React will just reuse that component instance and apply updates if needed. Re-rendering of the component will happen in the following example:
{isOpen ? <Component compClass="active" /> : <Component compClass="hidden" />}
Under the hood, React understands that in both cases of the ternary expression same component type is used. So it decides to re-render the component and change the props.
This is different compared to this example:
{isOpen ? <Component compClass="active" /> : null}
Since in this ternary expression we are dealing with different types, React will decide to unmount the previous component and mount the new one (based on isOpen
condition).
Keys
In the previous examples, we have seen that React identifies component instances based on their type. Another way to identify instances is using their key
pseudo-prop. key
is an instruction to React, and will never be passed through to the actual component. React uses thekey
prop as a unique identifier to differentiate specific instances of a component type.
Keys are mainly used when rendering lists. The keys must be some form of unique value from the data and not an array index as this could cause inconsistencies in rendering. Keys can be used outside of lists as well. If we add a key
prop to any React component, it will be used to indicate its identity. If this value is changed, React will destroy the old component instance and create new ones
Conclusion
The rendering process in React is a complex subject, but it is important to understand the basic ideas and concepts behind it. With a better understanding of rendering principles, you can start optimizing and building better React applications.