React: Basics

React is a JavaScript library that enables developers to build interactive UIs. React is a flexible and powerful ecosystem that can be used to build everything from mobile applications to large, enterprise web applications. It can also be embedded within other application frameworks and used for specific components within those applications.

I want to start small, though, and walk through building a very basic Hello World-type application with React, as well as some React fundamentals in this post. Later posts in this series will dig into specific programming patterns within the React ecosystem, but understanding those patterns and the nuances needed to achieve certain behaviors will be much easier if we have a good foundation for how React works. So let's start there.

Hello React

A basic React application isn't actually very complicated. We can start with an HTML file, and within the <body></body> tags include the following code:

<div id="root"></div>

<!-- Load React and React DOM -->
<script src="https://unpkg.com/react@18/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js" crossorigin></script>

<!-- Load our custom script -->
<script src="./index.js"></script>

The first thing we need in our markup is an empty <div> with the id of root. This is where our React application is going to load.

Next, we load up React and React DOM script files. In this case, we're dropping them into <script> elements through a CDN, just like we would Bootstrap or any other old school globally available JavaScript library. The first file, react.js, is the core React file, which gets used everywhere that we want to use React, whether that's in a web app, a mobile app, or somewhere else. We then load a second file, react-dom.js, because this is a web application. I'll dig into what React DOM does very shortly.

Finally, we load an index.js script file that we will create and save to our file system. Within index.js, we're going to have the following code:

// index.js
"use strict";

// 1. Create a React element
const element = React.createElement(
  "p",
  {
    id: "howdy"
  },
  "Howdy World!"
);

// 2. Create and embed our React DOM
const container = document.querySelector('#root');
const root = ReactDOM.createRoot(container);

// 3. Render the React element in our React DOM
root.render(element);

This renders as the following:

Nothing fancy, we're just using the two React libraries to render the following markup on the page:

<div id="root">
  <p id="howdy">Howdy World!</p>
</div>

We'll get to more elaborate uses of React later, but it's important to understand what's going on here. Under the hood, React uses JavaScript to render specified elements, giving them various attributes (like ids or classes or hrefs for <a> elements and srcs and alts for <img> elements) and content. We do this in the code example above when we call the following:

const element = React.createElement(
  "p",
  {
    id: "howdy"
  },
  "Howdy World!"
);

This function tells React that we want to create a "p" element, or a <p> paragraph tag, and to give it the id attribute with the value of "howdy" and that its contents should be the string "Howdy World!". We assign this React element as the value of the variable element. We then tell React where we want that element to render:

const container = document.querySelector("#root");
const root = ReactDOM.createRoot(container);
root.render(element);

This portion of the code does a few things. First it grabs the empty element we created in our HTML document, <div id="root"></div>, and assigns that DOM node as the value of the container variable. Then we call on the React DOM library to do its work.

For web applications, React uses what's called a virtual DOM to control the rendering of the React application. I won't dive too deep into the virtual DOM here, but it's fundamental to understand that the virtual DOM is the basis of React web applications, because a lot of the nuances and programming patterns that are standards for developing in React are to empower the virtual DOM to optimize performance for the application. Those patterns are what I'll be writing about in this blog post series. But, to the point of what we're doing in this basic example, we create the root element of the React DOM from our container DOM node, and then render our element inside of that React root element.

Another point that I'm making by stepping through this little code example is that React is built on plain, old fashioned JavaScript, like what most of the rest of this blog has been about to this point. In fact, we can even "build our own" React that achieves the following using plain JavaScript:

// index.js
"use strict";

function render(reactElement, containerDOMElement) {
  const { type, props, children } = reactElement;
  // 1. create the element from reactElement.type
  const element = document.createElement(type);

  // 2. for each reactElement.prop:
  for (let prop in props) {
    //   a. create an attribute from the key
    //   b. the value of that attribute should be the
    //      value of the prop
    element.setAttribute(prop, props[prop]);
  }
  // 3. the element's innerText should be the value of
  //    reactElement.children
  element.innerText = children;

  // 4. append the element to containerDomElement
  containerDOMElement.appendChild(element);
}

const reactElement = {
  type: "a",
  props: {
    href: "https://joeyreyes.dev",
    id: "howdy",
  },
  children: "Read More on joeyreyes.dev",
};

const containerDOMElement = document.querySelector("#root");

render(reactElement, containerDOMElement);

If we include this code in index.js, we'll be able to render out our reactElement link, as long as we also have <div id="root"></div> in our HTML. No need for the React libraries:

This is a very rudimentary implementation, and real React code is much more sophisticated, but at the end of the day it's not really too far off from what the React libraries are doing. What I'm trying to illustrate is that React is built on top of JavaScript fundamentals. Any dev approaching React with a decent understanding of JavaScript can go really far, but learning React first, and then seeking to understand the plain JavaScript it's built on will be much harder.

Hello JSX

So we have seen how to go about creating elements in React using the following:

const element = React.createElement(
  "p",
  {
    id: "howdy"
  },
  "Howdy World!"
);

This approach, where we write basic JavaScript to give instructions directly to React on how we want it to behave, is referred to as imperative programming. However, this manner of building in React is not very intuitive. If all we wanted was to render <p id="howdy">Howdy World!</p>, it would be nice if we could just write that element as is, and let React render it. Good news: JSX allows us to do just that. JSX is an extension of JavaScript that allows us to write something that looks closer to the markup we embed onto the page:

// index.js
"use strict";

// 1. Create a React element
const element = (
  <p id="howdy">Howdy world!</p>
);

// 2. Create and embed our React DOM
const container = document.querySelector("#root");
const root = ReactDOM.createRoot(container);

// 3. Render the React element in our React DOM
root.render(element);

We've replaced the React.createElement() function call with the following:

const element = (
  <p id="howdy">Howdy world!</p>
);

This is much, much nicer, and the result is the same:

Under the hood, JSX transpiles into a React.createElement() function call, with all of the JSX elements, their props, and their children as function call arguments. This manner of writing code that looks less like JavaScript instructions and more like what we want the final result to be is referred to as declarative programming.

Let's look at this transpilation in practice.

JSX:

const element = (
  <p id="howdy">Howdy world!</p>
);

Transpiled JavaScript:

const element = React.createElement(
  "p",
  {
    id: "howdy"
  },
  "Howdy World!"
);

We can even see this in action if we console.log(element);:

{
  "$$typeof": "Symbol(react.element)",
  "type": "p",
  "key": null,
  "ref": null,
  "props": {
    "id": "howdy",
    "children": "Howdy world!"
  },
  "_owner": null
}

Dynamic JSX

One of the more useful things about writing in JSX is that we can embed dynamic values into JSX markup. With a set of {} curly brackets, we can embed JavaScript expressions into static JSX, and the value returned from that expression gets rendered as part of the final content. For example:

// 1. Create a To Do List array
const toDoList = ["Sweep", "vacuum", "mop"];

// 2. Create a React element with the length of our To Do List array
const summary = (
  <p>Number of tasks remaining: {toDoList.length}</p>
);

This transpiles to the following JavaScript:

// 1. Create a To Do List array
const toDoList = ["Sweep", "vacuum", "mop"];

// 2. Create a React element with the length of our To Do List array
const summary = React.createElement(
  "p",
  {},
  "Number of tasks remaining: ",
  toDoList.length,
);

In regular JavaScript, you cannot pass a full statement as a function call argument, only expressions. It's the same with JSX: you can't pass a full statement within {} the curly brackets, only expressions. That's because, as we've seen, JSX transpiles down to plain JavaScript.

The children Prop

Typically if we create a button in HTML, we do it like so:

<button>Click Me</button>

Nothing fancy, we just place the Click Me text within the <button> tags.

If you're just starting React, it may not be clear how to pass text to a reusable button component. Say we have a Button component. One way we might pass it dynamic text is like so:

function Button({ contents }) {
  return (
    <button>{contents}</button>
  );
}

And then we call it like so:

root.render(
  <Button contents="Click Me" />
);

But that feels weird. Instead of passing an explicit contents prop (or something like that), we can simply pass the text Click Me within the <Button> tags, like we would with any HTML element, and then access that value through the special, reserved children prop. The markup for the component definition isn't really too different:

function Button({ children }) {
  return (
    <button>{children}</button>
  );
}

But when we actually make the element, it looks a bit more like an HTML button:

root.render(
  <Button>Click Me</Button>
);

If we console.log(Button); then we see our children prop automatically created:

{
  "$$typeof": "Symbol(react.element)",
  "type": "function Button({ children }) {\n  return /*#__PURE__*/(\n    React.createElement(\"button\", null, children));\n\n}",
  "key": null,
  "ref": null,
  "props": {
    "children": "Click Me"
  },
  "_owner": null
}

Digging a little bit deeper, the children prop isn't actually that special. In fact, if you wanted to pass it like any other prop, you could do something like the following:

function Button({ children }) {
  return (
    <button>{children}</button>
  );
}

root.render(
  <Button children="Click Me" />
);

This is equivalent to the code from the previous example. You could even pass the children prop in both places, but the value between the element tags would take precedent:

function Button({ children }) {
  return (
    <button>{children}</button>
  );
}

root.render(
  <Button children="Click Me">Don't Click Me</Button>
);

Result:

<div id="root">
  <button>Don't Click Me</button>
</div>

This, by the way, is our first time discussing props, but it certainly won't be the last. Stay tuned to future blog posts for a more thorough exploration into React's concepts of props and state!

Summary

React is a JavaScript library that enables developers to build interactive UIs. When React is used to build a web application, it employs a virtual DOM, which is an abstraction of the browser DOM, in order to optimize interactions and DOM manipulation mechanisms.

We can tell React what to render either with imperative JavaScript functions, or using declarative JSX markup. Both routes transpile down to DOM nodes that get embedded into React's virtual DOM. We can also interleave dynamic expressions into our JSX using {} curly brackets. But because JSX ultimately transpiles to the plain JavaScript code we would otherwise write directly, JSX is prone to the same patterns and limitations of regular JavaScript function calls, namely that we can only pass expressions and not full statements to JSX.

At first glance it might look like we have to explicitly declare the contents of an HTML element, like the text of a button, to make that HTML element dynamic in JSX, but we can simply use the special children prop and pass those contents in implicitly. We'll discuss props in a future blog post, but for now just know that the children prop is a special prop intended for use as the child contents of a JSX element.

Lastly, and I know I sound like a broken record here, because JSX transpiles down to plain JavaScript, we can always access a JSX element through the console to see how React breaks that element down as basic JavaScript objects. We can see the values of its children prop and other props, its key value (which will come into play when dealing with iteration), and the type of function that returns that element.