Coming back to the first, simple, code snippet from above, here's that same code snippet, this time using React:
import { useState } from 'react';
function App() {
const [outputText, setOutputText] = useState('Initial text');
function updateTextHandler() {
setOutputText('Text was changed!');
}
return (
<>
<button onClick={updateTextHandler}>Click to change text</button>
<p>{outputText}</p>
</>
);
}
This snippet performs the same operations as the first did with just vanilla JavaScript:
- Add an event listener to a button to listen for
click
events (now with some React-specific syntax: onClick={…})
.
- Replace the text of a paragraph with new text once the click on the button occurred.
Nonetheless, this code looks totally different—like a mixture of JavaScript and HTML. And indeed, React uses a syntax extension called JSX (i.e., JavaScript with embedded XML). For the moment, it's enough to understand that this JSX code will work because of a pre-processing step that's part of the build workflow of every React project.
Pre-processing means that certain tools, which are part of React projects, analyze and transform the code before its deployed. This allows for development-only syntax like JSX which would not work in the browser and is therefore transformed to regular JavaScript before deployment. (You'll get a thorough introduction into JSX in Chapter 2, Understanding React Components and JSX.)
In addition, the snippet shown above contains a React specific feature: State. State will be discussed in greater detail later in the book (Chapter 4, Working with Events and State will focus on handling events and state with React). For the moment, you can think of this state as a variable that, when changed, will trigger React to update the user interface in the browser.
What you see in the preceding example is the "declarative approach" used by React: You write your JavaScript logic (e.g., functions that should eventually be executed), and you combine that logic with the HTML code that should trigger it or that is affected by it. You don't write the instructions for selecting certain DOM elements or changing the text content of some DOM elements. Instead, with React and JSX, you focus on your JavaScript business logic and define the desired HTML output that should eventually be reached. This output can and typically will contain dynamic values that are derived inside of your main JavaScript code.
In the preceding example, outputText
is some state managed by React. In the code, the updateTextHandler
function is triggered upon a click, and the outputText
state value is set to a new string value ('Text was changed!'
) with help of the setOutputText
function. The exact details of what's going on here will be explored in Chapter 4.
The general idea, though, is that the state value is changed and, since it's being referenced in the last paragraph (<p>{outputText}</p>
), React outputs the current state value in that place in the actual DOM (and therefore on the actual web page). React will keep the paragraph updated, and therefore, whenever outputText
changes, React will select this paragraph element again and update its textContent
automatically.
This is the declarative approach in action. As a developer, you don't need to worry about the technical details (for example, selecting the paragraph, updating its textContent
). Instead, you will hand this work off to React. You will only need to focus on the desired end state(s) where the goal simply is to output the current value of outputText
in a specific place (i.e., in the second paragraph in this case) on the page. It's React's job of doing the "behind the scenes" work of getting to that result.
It turns out that this code snippet isn't shorter than the vanilla JavaScript one; indeed, it's actually even a bit longer. But that's only the case because this first snippet was deliberately kept simple and concise. In such cases, React actually adds a bit of overhead code. If that were your entire user interface, using React indeed wouldn't make too much sense. Again, this snippet was chosen because it allows us to see the differences at a glance. Things change if you take a look at the more complex vanilla JavaScript example from before) and compare that to its React alternative.
Note
Referenced code can be found on GitHub at http://packt.link/tLSLU and https://packt.link/YkpRa, respectively.
Figure 1.2. The code snippet from before, now implemented via React.
It's still not short because all the JSX code (i.e., the HTML output) is included in the JavaScript file. If you ignore pretty much the entire right side of that screenshot (since HTML was not part of the vanilla JavaScript files either), the React code gets much more concise. But, most importantly, if you take a closer look at all the React code (also in the first, shorter snippet), you will notice that there are absolutely no operations that would select DOM elements, create or insert DOM elements, or edit DOM elements.
And this is the core idea of React. You don't write down all the individual steps and instructions; instead, you focus on the "big picture" and the desired end state(s) of your page content. With React, you can merge your JavaScript and markup code without having to deal with the low-level instructions of interacting with the DOM like selecting elements via document.getElementById()
or similar operations.
Using this declarative approach, instead of the imperative approach with vanilla JavaScript, allows you, the developer, to focus on your core business logic and the different states of your HTML code. You don't need to define all the individual steps that have to be taken (like "adding an event listener", "selecting a paragraph", etc.), and this simplifies the development of complex user interfaces tremendously.
Note
It is worth emphasizing that React is not a great solution if you're working on a very simple user interface. If you can solve a problem with a few lines of vanilla JavaScript code, there is probably no strong reason to integrate React into the project.
Looking at React code for the first time, it can look very unfamiliar and strange. It's not what you're used to from JavaScript. Still, it is JavaScript—just enhanced with this JSX feature and various React-specific functionalities (like State). It may be less confusing if you remember that you typically define your user interface (i.e., your content and its structure) with HTML. You don't write step-by-step instructions there either, but rather create a nested tree structure with HTML tags. You express your content, the meaning of different elements, and the hierarchy of your user interface by using different HTML elements and by nesting HTML tags.
If you keep this in mind, the "traditional" (vanilla JavaScript) approach of manipulating the UI should seem rather odd. Why would you start defining low-level instructions like "insert a paragraph element below this button and set its text to <some text>" if you don't do that in HTML at all? React in the end brings back that HTML syntax, which is far more convenient when it comes to defining content and structure. With React, you can write dynamic JavaScript code side-by-side with the UI code (i.e., the HTML code) that is affected by it or related to it.
How React Manipulates the DOM
As mentioned earlier, when writing React code, you typically write it as shown above: You blend HTML with JavaScript code by using the JSX syntax extension.
It is worth pointing out that JSX code does not run like this in browsers. It instead needs to be pre-processed before deployment. The JSX code must be transformed to regular JavaScript code before being served to browsers. The next chapter will take a closer look at JSX and what it's transformed to. For the moment, though, simply keep in mind that JSX code must be transformed.
Nonetheless, it is worth knowing that the code to which JSX will be transformed will also not contain any DOM instructions. Instead, the transformed code will execute various utility methods and functions that are built-into React (in other words, those that are provided by the React package that needs to be added to every React project). Internally, React creates a virtual DOM-like tree structure that reflects the current state of the user interface. This book takes a closer look at this abstract, virtual DOM and how React works in Chapter 9, Behind the Scenes of React and Optimization Opportunities. Therefore, React (the library) splits its core logic across two main packages:
- The main
react
package
- And the
react-dom
package
The main react package is a third-party JavaScript library that needs to be imported into a project to use React's features (like JSX or state) there. It's this package that creates this virtual DOM and derives the current UI state. But you also need the react-dom
package in your project if you want to manipulate the DOM with React.
The react-dom
package, specifically the react-dom/client
part of that package, acts as a "translation bridge" between your React code, the internally generated virtual DOM, and the browser with its actual DOM that needs to be updated. It's the react-dom
package that will produce the actual DOM instructions that will select, update, delete, and create DOM elements.
This split exists because you can also use React with other target environments. A very popular and well-known alternative to the DOM (i.e., to the browser) would be React Native, which allows developers to build native mobile apps with help of React. With React Native, you also include the react package into your project, but in place of react-dom
, you would use the react-native
package. In this book, "React" refers to both the react package and the "bridge" packages (like react-dom).
Note
As mentioned earlier, this book focuses on React itself. The concepts explained in this book, therefore, will apply to both web browsers and websites as well as mobile devices. Nonetheless, all examples will focus on the web and react-DOM since that avoids introducing extra complexity.