How React Actually Works: The Magic Behind the Curtain 🪄
Ever wondered how React handles the magical transformation of your JSX into a functioning app? Let’s dive into the nitty-gritty of React components, elements, and instances. Grab your coffee ☕️, and let’s get started!
JSX to React Elements: The Inside Story 🧐
React developers love JSX for its simplicity and expressiveness. But underneath that HTML-like syntax, there’s a lot happening. Let’s break it down with a simple example.
const greeting = <div>Hello, world!</div>;
To React, this line is more than just pretty syntax. JSX gets transformed into a call to React.createElement
. Here’s what’s really going on:
const greeting = React.createElement(
'div',
null,
'Hello, world!'
);
Every call to React.createElement
returns a React element. This element is just a plain JavaScript object with some special properties:
{
type: 'div',
key: null,
ref: null,
props: {
children: 'Hello, world!'
},
$$typeof: Symbol.for('react.element')
}
The Breakdown 🕵️♂️
Type: The type of the element, like
'div'
.Key and ref: Special props used internally by React (we'll get to them in a bit).
Props: Includes
children
and other properties.$$typeof: An internal property used by React to identify this object as a React element.
Want to see this in action? Head over to the BabelJS REPL and try converting some JSX yourself! It’s a great way to understand how React elements are created under the hood.
What’s So Special Aboutkey
andref
? 🔑
Key: Helps React identify which items have changed, are added, or are removed. Ideal for list rendering.
Ref: Provides a way to access DOM nodes or React elements created in the
render
method. Super handy for managing focus, animations, or integrating with third-party DOM libraries.
These React elements are the building blocks of your application. They are lightweight representations of what you want to see on the screen.
React Components: Building Blocks 🧱
React components are like blueprints for your UI. They can be functional or class-based:
Functional Components 🏃
A functional component is just a plain JavaScript function that returns a React element:
const Welcome = () => <h1>Welcome to React!</h1>;
console.log(Welcome()); // Just returns an element
When used in JSX (<Welcome />
), React calls this function to get the element tree.
Class Components 🎩
A class component extends React.Component
and has a render
method that returns an element tree:
class Welcome extends React.Component {
render() {
return <h1>Welcome to React!</h1>;
}
}
React creates an instance of this class to manage the component's lifecycle. It’s like having a factory producing these elements with built-in methods for mounting, updating, and unmounting.
Component Instances: The React Lifecycle 🚀
When you use a class component, React creates an instance of it. Each instance has its own lifecycle methods (like componentDidMount
, componentWillUnmount
), state, and access to the this
keyword.
Think of it as React setting up a small backstage crew for each component, ready to handle everything from setting up the stage (mounting) to cleaning up after the show (unmounting).
But how does React efficiently manage updates to these instances when the state or props change? This is where React’s reconciliation process comes into play.
Reconciliation: React’s Secret Recipe 🍲
React manages updates through a process called reconciliation.
React creates a virtual DOM (a lightweight copy of the actual DOM).
When state or props change, React generates a new virtual DOM tree.
React compares the new tree with the old one to determine the minimal set of changes needed.
The Diffing Algorithm: React’s Detective 🕵️♀️
React's diffing algorithm efficiently figures out which parts of the tree have changed. It’s like playing spot-the-difference but way faster and with fewer mistakes.
Here’s how it works:
Element Type: If the types of two elements are different, React tears down the old element and builds the new one from scratch.
Keys: If elements are of the same type but have different keys, React treats them as different elements. This is why using unique keys (like IDs) instead of indexes is crucial.
Example:
const oldList = [
{ id: 1, name: 'Apple' },
{ id: 2, name: 'Banana' }
];
const newList = [
{ id: 1, name: 'Apple' },
{ id: 3, name: 'Cherry' }
];
const listItems = oldList.map(item =>
<li key={item.id}>{item.name}</li>
);
If we update oldList
to newList
, React knows only to replace the Banana
item with Cherry
because their keys differ. This makes updates blazingly fast. 🔥
But how do these changes get applied to the actual screen? This is where rendering comes into play.
Rendering: Bringing It to Life 🌟
Finally, rendering is where ReactDOM (or React Native) takes over. React itself doesn’t render, it defines the components and elements. ReactDOM performs the actual rendering on the web.
Rendering kicks off reconciliation, generates the element tree, and updates the real DOM with minimal changes. It’s like React handing over the blueprint to a master builder, who then constructs the final masterpiece on your screen.
Wrapping It Up with a Bow 🎁
React might seem like magic, but it’s built on solid principles. From transforming JSX into React elements, to efficiently updating the DOM with reconciliation and diffing algorithms, React makes building UIs a breeze.
Keep coding, keep exploring, and enjoy the journey through React’s magical world! ✨👩💻👨💻
Subscribe to my newsletter
Read articles from Mohammed Musaraf directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Mohammed Musaraf
Mohammed Musaraf
Solving Problems with Technology, Exploring Tech Breakthroughs.