It is safe to say that React is the most widely used JavaScript library for building user interfaces. The core concept behind React is to provide a flexible, efficient, and optimized way to manage DOM manipulations—such as adding and removing elements on a webpage—effortlessly. React abstracts the complexities of creating and updating HTML elements, allowing you to concentrate on the most crucial task: building user interfaces.
Initially, React was primarily used for DOM manipulation, but its capabilities have expanded significantly with recent updates, including the introduction of server components. In this article, I aim to provide an overview of how React operates and why it is exceptionally efficient for DOM manipulations.
React addresses the challenge of creating complex user experiences by decomposing application features and models into smaller, reusable components. This modularity enables you to easily assemble intricate user interfaces, simply by combining smaller, self-contained components that perform a single task exceptionally well. As a result, your code becomes more organized and easier to maintain, promoting code quality and reuse.
React Building Blocks
Before we delve into the internal workings of React, it’s important to discuss some key concepts that will be frequently referenced throughout this article. As a React developer, grasping these concepts will significantly enhance your understanding of React and its underlying mechanics.
Document Object Model (DOM)
The DOM represents the current webpage rendered in your browser and serves as a tree-like structure that organizes the elements of the page. You can think of the DOM as a fully grown tree, complete with roots, branches, and leaves. At the top of this tree is the document, which acts as the root, holding everything together. The document has a single child node, the element, which forms the foundation of the subtree that contains all other elements of the webpage.
Virtual DOM (VDOM)
The Virtual DOM is a JavaScript object representation of the actual (browser) DOM, stored in memory. This JavaScript object is designed to mimic the structure of the real DOM. The key idea is that since the Virtual DOM is a replica of the actual DOM, any changes we want to make to the browser DOM must first be applied to the Virtual DOM. Afterwards, we can efficiently update only the specific parts of the browser DOM that need to be changed. We will explore this concept in more detail later. For now, let’s look at a sample Virtual DOM representation of an actual HTML document.
<div> <h1>Hi</h1> <p>React is awesome</p>
</div>
{ type: 'div', children: [ { type: 'h1', text: 'Hi' }, { type: 'p', text: 'React is awesome' } ]
}
JavaScript XML (JSX)
JSX allows us to write JavaScript code that resembles HTML, making it easier to create elements compared to calling the React.createElement() method each time we need to generate a new element. However, React itself does not understand JSX natively. To bridge this gap, a transpiler such as Babel is required to convert JSX code into standard JavaScript syntax, which consists of a series of calls to React.createElement() method.
<div> <p>React is awesome</p>
</div>
React.createElement("div", null, React.createElement("p", null, "React is awesome"));
This process essentially reads the JSX code and replaces the content within the tags with calls to React.createElement() method. It passes the tag name, props, and children as parameters to this method. The React.createElement() method then creates an object from its arguments and attaches it as a node within the Virtual DOM.
{ type: 'div', children: [ { type: 'p', text: 'React is awesome' } ]
}
React Internals
React essentially has three key/crucial components that make up the library.
- React core
- React Render and
- React reconciler
React Core
React Core encompasses all the APIs necessary for creating React components. This is where you will find the createElement() method, along with various other methods used for state management and communication between components.
React Render
The Render process is responsible for rendering React components across different platforms, including web and mobile. It manages all the steps involved in displaying the React components for user viewing.
React Reconciler
The Reconciler is where the “magic” of React happens. React’s efficiency stems from its reconciliation process, which includes all the activities that must take place before React updates the browser’s DOM.
React Component
A React component is a reusable piece of code that encapsulates part of the user interface and its functionality. Components can be thought of as the building blocks of a React application. They allow developers to break down the UI into independent, reusable parts, which can be managed separately.
Example of a React Component
import React from 'react'; const Greeting = ({ name }) => { return <h1>Hello, {name}!</h1>; }; export default Greeting;
In this example, the Greeting is a functional component that takes a name prop and renders a message. This component can be reused throughout the application, allowing for consistency and reducing the amount of repetitive code.
React components also have a lifecycle, which provides hooks or methods that you can use to run code at specific stages of a component’s life, such as when it mounts, updates, or unmounts. This lifecycle allows developers to encapsulate logic pertinent to a component’s existence. Learn more about React components and lifecycle methods.
Component States vs Props
In React, state and props are both used to manage data and dictate the behaviour of components, but they serve different purposes and have distinct characteristics.
Props(Propertise)
Props are a way to pass data from a parent component to a child component. They are immutable from the child’s perspective. Props are set by the parent and can consist of any data type, including strings, numbers, arrays, functions, or even other components. Props are data properties a child component inherits from its parent component. Props are for passing data and callbacks between components, and they are read-only within the child component.
State
State is a way for a component to manage its own data internally. State is mutable, meaning it can be changed within the component. State represents the current status of a component and can change over time, usually as a result of user actions or API responses. State is for managing dynamic data within a component, and it can change over time, causing the component to re-render.
Importance of Componentizing Your Application
Reusability: Components can be reused across different parts of the application. This reduces duplication of code and makes it easier to maintain.
Separation of Concerns: By organizing your application into components, each component can focus on a specific concern or functionality. This separation makes your codebase easier to understand and maintain.
Improved Testing: Smaller, isolated components are easier to test. You can write unit tests for individual components without needing to consider the entire application context.
Simpler Maintenance: When a component is well-defined, you can make changes to it without affecting other parts of the application. This modularity facilitates collaboration among developers and speeds up the development process.
Dynamic Rendering: Components can manage their own state and lifecycle events, allowing for the dynamic behaviour of your application. This is critical for building interactive UIs.
Componentizing your application in React provides numerous benefits, including reusability, maintainability, performance, and better collaboration across development teams. This approach aligns with best practices in software development, allowing for cleaner, more organized, and efficient code.
How React Works
So far, we have explored the components and concepts that make up the React framework. Now, let’s discuss how all these elements fit together and interact with one another.
The Initial Setup
Imagine React as a bustling city. Each component is like a building in this city. When you first render a React application, it’s like constructing all the buildings based on the blueprints (your components). Each building (component) can have its own unique design (state) and can change over time.
State and Props Changes
Now, let’s say something happens that necessitates a change in one of the buildings. This could be a renovation (state change) or a new tenant moving in (props change).
When a component’s state or props change, here’s what happens:
- Event Trigger: An event occurs (like a user clicking a button) that triggers a change in state or props.
- State Update: React calls the setState function (for class components) or the state updater function (for functional components using hooks). This is like sending a notification to the city planning department that a building needs an update.
Component Lifecycle
Once the state or props are updated, the component goes through a lifecycle process:
- Re-rendering: React determines that the component needs to re-render. It’s akin to the building contractor getting the green light to start renovations.
- Render Phase: React calls the render method (or the functional component) to get the new representation of the component. This is like the architect drawing up new plans for the building.
The Reconciliation Process
Now, let’s talk about reconciliation, which is how React decides what to update in the actual DOM. Think of it like a city inspector who checks the new plans against the existing buildings.
Virtual DOM: React uses a Virtual DOM, which is a lightweight copy of the actual DOM. When you render a component, React first creates a new Virtual DOM representation of it. This is like having a detailed model of the city that you can manipulate quickly without affecting the real city.
Diffing Algorithm: React then compares the new Virtual DOM with the previous version (like the city inspector comparing old plans with new ones). This process is called “diffing.” It checks for changes and determines what actually needs to be updated in the real DOM.
Batch Updates: Instead of updating the DOM immediately, React batches these updates. This is like scheduling construction work to minimize disruption in the city. React collects all changes and applies them in one go, making the process more efficient.
Actual DOM Update
Once React knows what has changed, it updates the actual DOM. This is like the construction crew arriving to make the necessary changes to the buildings in the city.
Summary
So, to summarize the entire process:
- State/Props Change: An event triggers a change in the component’s state or props.
- Re-rendering: React prepares to re-render the component.
- Virtual DOM Creation: A new Virtual DOM representation is created.
- Diffing: React compares the new Virtual DOM with the previous one to identify changes.
- Batch Updates: Changes are batched together to minimize DOM manipulation.
- Actual DOM Update: The real DOM is updated based on the identified changes.
In essence, React is like a well-organized city that efficiently manages its buildings (components) and renovations (updates) using a smart planning system (Virtual DOM).
Additionally, React provides a Context API that facilitates the sharing of global data across the component tree without the need to pass props down manually at every level. This is particularly useful for managing themes or user authentication.
To further optimize rendering performance, React supports techniques such as shouldComponentUpdate, React.memo, and hooks like useMemo and useCallback. These tools allow developers to prevent unnecessary re-renders and enhance application responsiveness.
Error handling in React can be managed through the use of error boundaries. These are special components that catch JavaScript errors in their child component tree, allowing for graceful recovery and a more robust user experience.
Furthermore, React supports server-side rendering (SSR), which enables components to be rendered on the server before being sent to the client. SSR can improve both performance and SEO, as the initial page load can provide fully rendered HTML to the browser, reducing the time to first contentful paint.
In summary, React streamlines UI development using JSX, optimizes performance through the virtual DOM and diffing algorithm, provides a rich lifecycle and context features for component management, and includes robust error handling, all while supporting server-side rendering for enhanced performance and SEO.
Conclusion
React provides a significantly more optimized approach to DOM manipulation compared to traditional web development methods, which often involve direct manipulation of the DOM. This can become cumbersome and error-prone as applications scale and complexity increase.
With React, you adopt a declarative programming style, allowing you to describe what the UI should look like at any given moment. React then efficiently manages the updates to the DOM, ensuring that your application remains performant and responsive.
Understanding the inner workings of React is just as crucial as learning how to use it effectively. By gaining insights into React’s internals, developers can write, test, and deploy applications that not only fulfil their intended tasks but also do so with optimal efficiency.
Thank you for taking the time to read this post! If you have any questions or feedback, please feel free to leave a comment below, and I’ll get back to you as soon as possible. I look forward to hearing your thoughts. Until next time, happy coding!
Discover more from Coursity
Subscribe to get the latest posts sent to your email.