Chapter 15 Introduction to React

This chapter introduces the React JavaScript library. React is “a JavaScript library for building user interfaces” developed by Facebook (though it is released as open-source software). At its core, React allows you to dynamically generate and interact with the DOM, similar to what you might do with jQuery. However, React was created to make it much easier to define and manipulate lots of different parts of the DOM, and to do so quickly (in terms of computer speed). It does this by enabling you to declare a web app in terms of different components (think: “things” or “parts” of a web page) that can be independently managed—this lets you design and implement web apps at a higher level of abstraction. Moreover, components are usually associated with some set of data, and React will automatically “re-render” (show) the updated component when the data changes—and to do so in a computationally efficient manner.

React is currently the most popular “framework” for building large-scale web applications (its chief competitors being Angular and Vue.js, though there are scores of similar frameworks that are also used. Check out TodoMVC for an example of the same application in each!). This means that React is generally very well documented; there are hundreds of tutorials, videos, and examples for building React applications (of which this chapter will be yet another). Some general resources are included at the end of the chapter, but in general we recommend you start with the official documentation, particularly the set of main concepts. Note that this book more or less follows the approach used by Facebooks’s Intro to React Tutorial.

React has gone through a number of different major versions in its short life. What’s more: the “style” in which React is written has also evolved over time as new features are added to both the React library and the JavaScript language. This book introduces and emphasizes an approach that prioritizes clarity of concepts and readability of code, rather than conciseness of syntax or use of advanced options, while also attempting to reflect the current practices in React development.

React is developed and maintained by Facebook (Meta). Facebook as a company has a long history of violating the Fair Housing Act, leaking or selling personal private data, allowing the spread of misinformation, enabling genocide and in general choosing profits over safety. You may be understandably concerned about whether learning and using React implicitly supports and thus condones this company and its behavior. Whenever you adopt any software tool or library, it is worth investigating who created it (and why) and how that may impact your own willingness to use it as a dependency.

15.1 Getting Set Up: Create React App

React is a JavaScript library similar to those discussed in previous chapters—you load the library and then can call upon its methods. However, React makes use of a significant amount of advanced and custom JavaScript syntax (described below), including extensive use of ES6 Modules. Thus in practice, developing React apps requires using a large number of different development tools—just getting setup and started is one of the major hurdles in learning React!

Luckily, Facebook does provide an amazing application which combines many of these these different development tools together. Create React App (CRA) is a command line application that generates scaffolding (“starter code”) for a React application, as well as provides built-in scripts to run, test, and deploy your code.

You can use Create React App to create a new React app by running the program and specifying a name for the folder you want the app to be created inside of. For example, the below command will create a new folder called my-app that contains all of the code and tools necessary to create a React app:

# Create a new React app project in a new `my-app` directory
# This may take a minute...
npx create-react-app my-app --use-npm

# Change into new project directory to run further commands
cd my-app
  • The npx command is installed alongside npm 5.2.0 and later, and can be used to (temporarily) install and run a global command in a single step. So rather than needing to use npm install -g create-react-app, you can just use npx.

  • If you specify the current directory (.) as the target folder name, Create React App will create a new react app in the current folder! This is useful when you want to create a React app inside an existing cloned GitHub repository.

  • The --use-npm argument is optional, but will make sure that your app uses npm to manage packages and not an alternate manager (such as yarn).

Running the Development Server

After you create a React app, you can use one of CRA’s provided scripts (the start script) to automatically start up a development webserver:

# Make sure you are in the project directory
cd path/to/project

# Run the development server script
npm start

This will launch the webserver, and open up a new browser window showing the rendered web page! This development webserver will automatically perform the following whenever you change the source code:

  1. It will transpile React code into pure JavaScript that can be run in the web browser (see JSX below).

  2. It will combine (bundle) all of the different code modules into a single file, and automatically inject that single file into the HTML (adding the <script> tag for you).

  3. It will display errors and warnings in your browser window—most errors will show up in the developer console, while fatal errors (e.g., syntax errors that crash the whole app) will be shown as an error web page. Note that the Chrome React Developer Tools will provide further error and debugging options.

  4. It will automatically reload the page whenever you change the source code! (Though it is often good to manually refresh things when testing, just to make sure).

All these capabilities are provided by a tool called webpack. Webpack is at its core a module bundler: it takes your complicated source code structure (lots of files) and transforms it into a brand new, “simplified” version (a single file). In the process, can automatically process and change that code—from automatic formatting and styling, to compiling or transpiling from one language to another. Webpack is a fairly complex build tool in its own right; however, Create React App provides its own configuration to this program so that you don’t have to do anything with it directly!

In order to stop the server, hit ctrl-c on the command line.

Important: As of 2018, there is an issue with Create React App on Windows using Git Bash where hitting ctrl-c won’t actually stop the server—if you try to run it again, you’ll get an error about the port still being occupied. Current work-arounds are to use the Task Manager to end the node process, or to instead use the Linux subsystem for Windows.

Project Structure

A newly created React app will contain a number of different files, including the following:

  • The public/index.html file is the home page for your application when webpack builds the app. If you look at this file, you’ll notice that it doesn’t include any CSS <link> or JavaScript <script> elements—the CSS and JavaScript for your React app will be automatically injected into the HTML file when the app is built and run through the development server. The CSS and JavaScript files will be found in the src/ folder. Similarly, all of the content of the page will be defined as JavaScript components—you’ll be creating the entire page in JavaScript files. Thus in practice, you rarely modify the index.html file (beyond changing the metadata such as the <title> and favicon).

    Notice that the index.html file contains almost no HTML content— just the <head>, the <body> and a single <div id="root">. That is because your entire app will be defined using React JavaScrip code; this content will then be injected into the #root element.

    Overall the public/ folder is where you put assets that you want to be available to your page but aren’t loaded and compiled through the JavaScript (i.e., they’re not “source code”). For example, this is where you would put an img/ folder to hold pictures to show. The public/ folder will be the “root” of the webpage; a file found at public/img/picture.jpg would be referenced in your DOM as img/picture.jpg—using a path relative to the public/ folder. See Using the public folder for details.

  • The src/ folder contains the source code for your React app. The app is started by the index.js script, but this script imports and uses other JavaScript modules (such as the provided App.js script, which defines the “App” module). In general, you’ll code React apps by implementing components in one or more modules (e.g., App.js, AboutPage.js), which will then be imported and used by the index.js file. See the Components section below for details.

    • The reportWebVitals.js and setupTests.js are provided by default to better support testing and performance measurements. This text doesn’t cover these features; you can ignore or delete them for now.
  • The src/index.css and src/App.css are both CSS files used to style the entire page and the individual “App” component respectively. Notice that the CSS files are imported from inside the JavaScript files (with e.g., import './index.css' in the src/index.js file). This is because the webpack build system that Create React App uses to run apps knows how to load CSS files, so you can “include” them through the JavaScript rather than needing to link them in the HTML.

    If you want to load an external stylesheet, such as Bootstrap or Font-Awesome, you can still do that by modifying the public/index.html file—but it’s better to install them as modules and import them through the JavaScript. See the documentation for Adding Bootstrap for an example.

    • Remember that all CSS is global—rules specified in any .css file will apply to the entire app (i.e., the App.css rules will also apply to components other than those in App.js). The CSS is broken into multiple files to try and keep rules organized. You can instead put all your CSS rules into a single file, using naming schema or other techniques to keep them organized. If you are interested in ways to keep the CSS from being applied globally, look into using CSS in JS.
  • The README.md contains a copy of the User Guide (which can also be found online), focused on “how to perform common tasks”. the user guide is very thorough—if you want to do something within the CRA framework, try searching this first, as there is probably a guide and example code for you to use!

Overall, you’ll primarily be working with the .js and .css files found in the src/ folder as you develop a React app.

Alternatives to Create React App

While Create React App is the easiest and recommended way to get started with a React application, it’s also possible to try out React just by including external JavaScript libraries in the same way you would do with jQuery. Note that this approach is not recommended, especially for production systems—it will run much slower, and be harder to develop (you don’t get the useful debugging features of Create React App’s development server).

<!-- Load the React library scripts (there are two) -->
<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>

The React library actually consists of two libraries that are loaded in the previous code snippet. The react.development.js script is the library used to create React components, while the react-dom.development.js script is the library that handles DOM manipulation. These are both “development” versions of the scripts; you would want to use “production” versions for a production environment.

15.2 JSX

At its core, React is simply a DOM creation and rendering library—it allows you to declare new DOM elements (e.g., make a new <h1>) and then add that element to the webpage. This is similar to the process you might do with the DOM or the jQuery library:

//Create and render a new `<h1>` element using DOM methods
const element = document.createElement('hi'); //create element
element.id = 'hello'; //specify attributes
element.classList.add('my-class');
element.textContent = 'Hello World!'; //specify content

document.getElementById('root').appendChild(element);

The React library provides a set of functions that do similar work:

//Import required functions from the React libraries
import React from 'react';
import ReactDOM from 'react-dom/client';

//Create a new `<h1>` element using React methods. This function takes
//arguments indicating what the element should be
const msgElem = React.createElement(
  //html tag
  'h1',
  //object of attributes
  { id: 'hello', className: 'myClass' },
  //content
  'Hello World!'
);

//Create a "React root" out of the `#root` elemment
const root = ReactDOM.createRoot(document.getElementById('root'));

//render the React element at that root
root.render(element)

React’s React.createElement() lets you create an element (a “React element”, not a pure DOM element—they are different!) by specifying the element type/tag, the content, and an object that maps from attribute names to values—all as a single statement. The ReactDOM.createRoot() function creates a context in which a React element can then be rendered (shown on the screen)—usually placing that in the div#root on the page. The root’s render() method will then take a particular React element and insert it as the child content of the React root element . Note that root.render() function replaces the content of the root DOM element—it’s a “replace” not an “append” operation.

  • Notice that the class attribute is specified with the className property. This is because class is a reserved keyword in JavaScript (it declares a class).

The createRoot() function was introduced with React v18 (March 2022). Previous versions of React used the ReactDOM.render() function directly instead of explicitly creating a root context. Using ReactDOM.render() will cause your app to function as if using React v17, preventing some advanced features from working.

The React.createElement() function is wordy and awkward to type, particularly if you’ll be defining a lot of DOM elements (and in React, you define the entire application in JavaScript!). Thus React lets you create and define elements using JSX. JSX is a syntax extension to JavaScript: it provide a new way of writing the language—in particular, the ability to write bare HTML tags (finally!):

//Create a new `<h1>` element using JSX syntax
const element = <h1 id="hello" className="myClass">Hello World</h1>;

//Create a "React root" out of the `#root` elemment
//then render the React element at that root
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(element)

The value assigned to the element variable is not a String (it has no quotes around it). Instead it is a syntactical shortcut for calling the React.createElement() function—the HTML-like syntax can be automatically translated into the actual JavaScript for the method call, allowing you to write the simpler version.

  • Notice that there is still a semicolon (;) at the end of the first line. You’re not actually writing HTML, but defining a JavaScript value using syntax that mostly looks like HTML!

  • JSX can be used to define nested elements; you are not limited to a single element:

    //JSX can define nested elements
    const header = (
      <header className="banner">
        <h1>Hello world!</h1>
      </header>
    );

    It’s very common to put elements on their own lines, intended as if you were writing HTML directly. If you do so, you’ll need to surround the entire JSX expression in parentheses () to tell the interpreter that the line break shouldn’t be read as implying a semicolon!

  • Note that similar to XML, JSX syntax requires that all elements be closed; thus you need to use a closing slash at the end of empty elements:

    //JSX elements must be closed
    const picture = <img src="my_picture.png" alt="a picture" />

Since JSX is not actually valid JavaScript (you can’t normally include bare HTML), you need to “translate” it into real JavaScript so that the browser can understand and execute it. This process is called transpiling, and is usually performed using a compiler program called Babel. Babel is yet another piece of the toolchain used to create React applications; however, all the details about transpiling with Babel are automatically handled by the Create React App scaffolding. “Transpile with Babel” is one of the steps performed by Webpack, which is included and run from the scripts provided by Create React App (whew!).

In short: you write JSX code, and when you run your app with Create React App it will be turned into regular old JavaScript code that your browser can actually understand.

Inline Expressions

JSX allows you to include JavaScript expressions (such as variables you want to reference) directly inside of the HTML-like syntax. You do this by putting the expression inside of curly braces ({}):

const message = "Hello world!";
const element = <h1>{message}</h1>;

When the element is rendered, the expression and the surrounding braces will be replaced by the value of the expression. So the above JSX would be rendered as the HTML: <h1>Hello World</h1>.

You can put any arbitrary expression inside of the {}—that is, any JavaScript code that resolves to a single value (think: anything that you could assign to a variable). This can include math operations, function calls, ternary expressions, or even an anonymous function values (see the next chapter for examples):

//Including an inline expression in JSX. The expressions can be mixed directly
//into the HTML
const element = <p>A leap year has {(365 + 1) * 24 * 60} minutes!</p>;

You can use inline expressions most anywhere in JSX, including as the value of an attribute. You can even declare element attributes as an object and then use the spread operator (...) to specify them as an inline expression.

//Define a JavaScript variable
const imgUrl = 'path/to/my_picture.png';

//Assign that variable to an element attribute
const pic = <img src={imgUrl} alt="A picture" />;

//Define an object of element attributes
const attributes = {
    src: 'path/to/other_picture.png',
    alt: 'Another picture'
};

//A DOM element that includes those attributes, among others.
//The spread operator applies the property names as attributes of the element.
const otherPic = <img {...attributes} className="my-class" />;

Notice that when specifying a JavaScript expression as the value of an attribute (as with the src attribute of an <img>), you do not include quotes around the {}. The value of the inline expression is already a string, and it is that string that you’re assigning to the attribute. Inline expressions aren’t “copy-pasted” into the HTML-style of JSX; rather the values of those expressions are assigned to the object properties represented by the HTML-style attributes.

Importantly, the elements defined with JSX are also valid JavaScript expressions! Thus you can use inline expressions to include one element inside of another.

//Define variable content to display. One is a string, one is a React element.
const greetingString = "Good morning!";
const iconElem = <img src="sun.png" alt="A wide awake sun" />;

//Conditionally change values based on an `isEvening` value (defined elsewhere)
if(isEvening) {
    greetingString = "Good evening!";
    iconElem = <img src="moon.png" alt="A dozing moon" />;
}

//Include the variables inline in another React element
//Notice how the `icon` element is included as a child of the `<header>`
const header = (
    <header>
      <h1>{greetingString}</h1>
      {iconElem}
    </header>
);

If you include an array of React elements in an inline expression, those those elements will be interpreted as all being children of the parent element (siblings of each other):

//An array of React elements. Could also produce via a loop or function
const listItems = [<li>lions</li>, <li>tigers</li>, <li>bears</li>];

//Include the array as an inline expression; each `<li>` element will be
//included as a child of the `<ul>`
const list = (
  <ul>
    {listItems}
  </ul>
);

The above sample would produce the HTML:

<ul>
    <li>lions</li>
    <li>tigers</li>
    <li>bears</li>
</ul>

This is particularly useful when using a variable number of elements, or generating elements directly from data (see the Props and Composition section below).

As a final note: you cannot include HTML comments (<!-- -->) inside of JSX, as this isn’t actually an element type. You also can’t use inline JavaScript comments (//), because even multi-line JSX is still a single expression (that’s why the <header> example above is written in parentheses). Thus in order to include a comment in JSX, you can use a JavaScript block comment (/* */) inside of an inline expression:

const main = (
    <main>
      { /* A comment: the main part of the page goes here... */ }
    </main>
)

15.3 React Components

When creating React applications, you use JSX to define the HTML content of your webpage directly in the JavaScript. However, writing all of the HTML out as one giant JSX expression doesn’t provide much advantage over just putting that content in .html files in the first place.

Instead, the React library encourages and enables you to describe your web page in terms of components, instead of just HTML elements. In this context, a Component is a “piece” of a web page. For example, in a social media application, each “status post” on a timeline might be a different Component, the “like” button may be its own Component, the “what are you thinking about?” form would be a Component, the page navigation would be a Component, and so forth. Thus you can almost think of a Component as a semantic abstraction of a <div> element—a “chunk” of the page. But like with <div>s, a Component can be seen as made of of other Components—you might have a “Timeline” Component that is made up of many “StatusPost” Components, and each StatusPost Component may include a “ReactionButton” Component.

Thinking about a page in terms of Components instead of HTML elements can make it much easier to design and reason about your page. You don’t need to worry about the code syntax or the specific elements to use, you can just think of your page in terms of its overall layout and content—what needs to be on the page, and how that content is organized—the same way you might mock up a web page in a graphic design program. See the documentation article Thinking in React for an example of how to design a page in terms of Components.

React enables you to implement your page in terms of explicit Components by providing a way for you to define your own XML elements! Thus you can define a <Timeline> element, which has multiple <StatusPost> elements as children. Components will be able to be included directly in your JSX, made up of and mixed in with standard HTML elements.

An example page broken up into components. Example from http://coenraets.org/blog/2014/12/sample-mobile-application-with-react-and-cordova/

<App>
  <HomePage>
    <Header />
    <SearchBar />
    <EmployeeList>
      <EmployeeListItem person="James King" />
      <EmployeeListItem person="Julie Taylor" />
      <EmployeeListItem person="Eugene Lee" />
    </EmployList>
  </HomePage>
  <EmployeePage>
    <Header />
    ...
  </EmployeePage>
</App>

React components are most commonly defined using function components: JavaScript functions that return the DOM elements (the JSX) that will be rendered on the screen to show that Component:

//Define a Component representing information about a user
function UserInfoCard(props) {
  //This is an everyday function; you can include any code you want here
  //including variables, if statements, loops, etc
  const name = "Ethel";
  const descriptor = "Aardvark";

  //Return a React element (JSX) that is how the Component will appear
  return (
    <div>
      <h1>{name}</h1>
      <p>Hello, my name is {name} and I am a {descriptor}</p>
    </div>
  )
}

//Create a new value (a React element)
//This syntax in effect "calls" the function
const infoElement = <UserInfoCard />;

//Show the element in the web page (inside #root)
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(infoElement);

This function defines a new Component (think: a new HTML element!) called UserInfoCard. Things to note about defining this component:

  1. A Component function must be named starting with a Capital letter (and usually in CamelCase). This is to distinguish a custom component from a “normal” React element.

    • If you see a function with a Capitalized name, know that it’s actually a defining a Component!

    • It’s also possible for function components to be defined as anonymous arrow functions assigned to const variables:

      const Example = (props) => {
        return <div>...</div>
      }

      This provides some benefits in terms of scoping (because of the arrow function), but otherwise makes Components even harder to read—its not as readily obviously that this is a function and a Component. You should stick to defining component functions using the function keyword (and not arrow functions) for readability, but be aware of the alternate approach which is used in some libraries and examples.

  2. Component functions take in a single argument representing the props (discussed below). As such by convention it is always named props (with an s!). While it’s possible to omit this argument if it isn’t used (as you can with all JavaScript functions), best practice is to always include it.

  3. A component function is a regular function like any other that you’ve written. That means that it can and often does do more than just return a single value! You can include additional code statements (variable declarations, if statements, loops, etc) in order to calculate the values you’d like to include in inline expressions in the returned JSX.

    Style requirement: perform any complex logic—including functional looping such as with map() or filter()—outside of the return statement. Keep the return statement as simple as possible so it remains readable—and more importantly debuggable (so you can use things like console.log statements to check intermediate values before they are returned). NEVER include an inline callback function so that you have a return statement inside of a return statement!
  4. A Component function can only return a single React element (you can’t return more than one value at a time from a JavaScript function). However, that single React element can have as many children as you wish. It’s very common to “wrap” all of the content you want a component to include in a single <div> to return, as in the above example.

    If you don’t like want to add an extra div for some reason, it’s also possible to “wrap” content in a Fragment, which can be declared using a shortcut syntax that looks like an element with no name:

    function FragmentExample(props) {
      return (
        <>
          <p>First paragraph</p>
          <p>Second paragraph</p>
        </>
      )
    }

Once it has been defined, a Component can then be created in JSX using XML-style syntax, just like any other element (i.e., as <UserInfoCard />). In effect, this “calls” the function component in order to create it—you never call the function directly. You can use the created Component like any other React element, such as including it as a child of other HTML (e.g., nest it inside of a <div>). A Component thus encapsulates a chunk of HTML, and you can almost think of it as a “shortcut” for including that HTML.

Never call a component function as a function with MyComponent()! Always render as an element with <MyComponent/>.

Because Components are instantiated as React elements, that means that you can use them anywhere you would use a normal HTML element—including in the returned value of another component! This is called composing components—you are specifying that one component is “composed” (made up of) others:

//A component representing a message
function HelloMessage(props) {
  return <p>Hello World!</p>;
}

//A component representing another message
function GoodbyeMessage(props) {
  return <h1>See ya later!</h1>;
}

//A component that is composed of (renders) other components!
function MessageList(props) {
  return (
    <div>
      <h1>Messages for you</h1>
      <HelloMessage /> {/* include a HelloMessage component */}
      <GoodbyeMessage /> {/* include a GoodbyeMessage component */}
    </div>
  );
}

//Render an instance of the "top-level" ("outer") component
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<MessageList />);

In the above example, the MessageList Component is composed of (renders) instances of the HelloMessage and GoodbyeMessage Components; when the MessageList is displayed, the interpreter will create the two other components, rendering each in turn. In the end, all of the content displayed will be standard HTML elements—they’ve just been organized into Components.

  • Components are usually instantiated as empty elements, though it is possible to have them include child elements.

  • Notice that you can and do mix standard HTML elements and Components together; Components are just providing a useful abstraction for organizing content, logic, and data!

In practice, React applications are made up of many different Components—dozens or even hundreds depending on the size of the app! The “top-most” component is commonly called App (since it represents the whole “app”), with the App’s returned value instantiating further components, each of which represents a different part of the page.

The App component will represent the entirety of the website—all of your app’s content will be defined in React components (which will be rendered as children of the <App>). But since the React components will be inserted inside of the <div id="root"> element that is already in the index.html file, you do not ever include <body> or <head> elements in your React components—those elements are already defined in the HTML file itself, so don’t get created by React!

See Props and Composition below for examples of common ways that React components are composed.

Strict Mode

If you look at the starter code generated by Create React App, you will see a common pattern of “wrapping” a rendered <App> component in a <React.StrictMode> component:

import React from 'react';
import ReactDOM from 'react-dom/client';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

The StrictMode component is provided by React (it’s a property of the React object imported in the first line of the above example). Like the use strict declaration for JavaScript, this component enables a number of additional error checking that React can perform— though most of the issues it identifies are due to using out-of-date practices. It can be useful to use, though it will cause duplicated output of some console.log() statements and is not strictly necessary.

Component Organization

React applications will often have dozen of components, quickly growing more complex than the basic starting examples in the previous section. If you tried to define all of these components in a single index.js file, your code would quickly become unmanageable.

Thus individual components are usually defined inside of separate modules (files), and then imported by the modules that need them—other components or the root index.js file.

Recall that in order to make a variable or function (e.g., a Component) available to other modules, you will need to export that component from its own module, and then import that value elsewhere:

/* in Messages.js file */

//Export the defined components (as named exports)
export function HelloMessage() { /* ... */ }

export function GoodByeMessage() { /* ... */ }
/* in App.js file */

//Import components needed from other modules
import { HelloMessage, GoodbyeMessage } from `./Messages.js`; //named imports!

//Can also export as a _default_ export; common if the file has only one component
export default function App() {
    return (
        <div>
            {/* Use the imported components */}
            <HelloMessage />
            <GoodbyeMessage />
        </div>
    )
}
/* in index.js file */

//Import components needed from other modules
import App from `./App.js`; //default import!

//Render the imported component
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);

This structure allows you to have each component (or set of related components) in a different file, helping to organize your code.

  • This “export-import” structure implies a one-directional hierarchy: the Message components don’t know about the App component that will render them (they are self-contained). You should not have two files that import from each other!

  • The index.js file will usually just import a single component; that file stays very short and sweet. Put all of your component definitions in other files.

It is common to use the file system to further organize your component modules, such as grouping them into different folders, as in the below example:

src/
|-- components/
  |-- App.js
  |-- navigation/
    |-- NavBar.js
  |-- utils/
    |-- Alerts.js
    |-- Buttons.js
index.js

Note that a module such as Alerts.js might define a number of different “Alert” components to use; these could be imported into the NavBar.js or App.js modules as needed. The App.js component would be imported by the index.js file, which would contain the call to root.render().

However, it is not necessary to put each different component in a separate module; think about what kind of file organize will make it easy to find your code and help others to understand how your app is structured!

Deprecated Alternative: Class Components

In order versions and styles of React, it was alternatively possible to define components as classes (using ES6 Class syntax) that acts as a template or blueprint for that content. In particular, you define a class that inherits from the React.Component class—this inheritance means that the class you define IS A React Component!

//Define a class component representing information about a user
class UserInfo extends React.Component {
    //Components MUST override the `render()` function, which must return a
    //DOM element
    render() {
        //This is an everyday function; you can include any code you want here
        const name = "Ethel";
        const descriptor = "Aardvark";

        //Return a React element (JSX) that is how the component will appear
        return (
            <div>
                <h1>{name}</h1>
                <p>Hello, my name is {name} and I am a {descriptor}</p>
            </div>
        )
    }
}

//instantiate the class as a new value (a React element)
const infoElement = <UserInfo />;

//Show the element in the web page (inside #root)
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(infoElement);

This “class component” example is identical to the previous function component example—it takes the “body” of the function component and moves that into a (required) render() method.

  • Note that the React.Component class is often imported as a named import, so that you can refer to it directly:

    import {Component} from 'react';
    
    class MyComponent extends Component {
        render() {
            //...
        }
    }

Current best practices in React are to only use function components and to not use class components; class components provide no extra functionality, and can introduce some additional complexities (particularly for handling their state). However it is good to be aware of the option in case you run across it, and thinking about components as “classes” can be useful for understanding how they are defined and used.

Additionally, older versions of React included a function React.createClass() that took in an object whose properties were functions that would act as class methods (see here for details). Although this function has been removed, it’s worth being aware of in case you come across older examples of React code that you need to interpret or adapt.

15.4 Properties (props)

One of the main goals of using a library such as React is to support data-driven views—that is, the content that is rendered on the page can be based on some underlying data, providing a visual representation of that data. And if that underlying data changes, React can automatically and efficiently “re-render” the view, making the page dynamic.

You can specify the underlying data that a React component will be representing by specifying properties (usually just called props) for that component. Props are the “input parameters” to a component—they are the details you pass to it so that it knows what and how to render its content.

You pass a prop to a component when you instantiate it by specifying them as XML attributes (using name=value syntax)—the props are the “attributes” of the component!

//Passing a prop called `message` with value "Hello property"
const messageA = <MessageItem message="Hello property!" />;

//Passing a prop using an inline expression
const secret = "Shave and a haircut";
const messageB = <MessageItem message={secret} />;

//A component can accept multiple props
//This component takes in a `userName` prop as well as a `descriptor` prop
const userInfo = <UserInfo userName="Ethel" descriptor="Aardvark" />;
  • Prop names must be valid JavaScript identifiers, and so should be written in camelCase (like variable names).

Components are usually defined so that they expect a certain set of props—similar to how a function is defined to expect a certain set of arguments. It is possible to pass “extra” props to the Component (by specifying additional XML attributes), but if the component doesn’t expect those props it won’t do anything with them. Similarly, failing to pass an expected prop will likely result in errors—just like if you didn’t pass a function an expected argument!

For function components, the props are received as a single argument to the function (usually called props). This argument is a regular JavaScript object whose keys are the props’ “names”:

//Define a component representing information about a user
function UserInfo(props) {
  console.log(props) //you can see this is an object!

  //access the individual props from inside the argument object
  const userName = props.userName;
  const descriptor = props.descriptor;

  //can use props for logic or processing
  const userNameUpper = props.userName.toUpperCase();

  return (
    <div>
      <h1>{userNameUpper}</h1>
      <p>Hello, my name is {userName} and I am a {descriptor}</p>
    </div>
  )
}

const userInfo = <UserInfo userName="Ethel" descriptor="Aardvark" />;

Again, all props are stored in the argument object—you have to access them through that value.

  • To avoid having to type props.___ all the time, it’s common to use object destructuring to assign all of the props to variables in a single line:

    function UserInfo(props) {
        //assign the prop object properties to variables
        const {userName, descriptor} = props;
    
        return <h1>{userName}</h1>;
    }

    It’s also possible to specify the object destructuring in the argument declaration!

    //The props object passed in will be destructured into two argument variables
    function UserInfo({userName, descriptor}) {
        return <h1>{userName}</h1>;
    }

    This can help make it clear in the source code what props a component expects, though can make the code somewhat more difficult to parse (since there are so many parentheses and curly braces). However, to keep the idea of the props clear, all examples in this book will use a single, non-destructured props object.

Importantly, props can be any kind of value! This includes basic types such as Numbers or Strings, data structures such as Arrays or Objects, or even functions or other component instances!

//Props can be of any data type!

//Pass an array as a prop!
const array = [1,2,3,4,5];
const suitcase = <Suitcase luggageCombo={array} />;

//Pass a function as a prop (like a callback)!
function sayHello() {
    console.log('Hello world!');
}
const greeting = <Greeting callback={sayHello} />;

//Pass another Component as a prop (not common)!
const card = <HolidayCard message="Greetings world!" />
const gift = <Gift toMsg="Ethel", fromMsg={card} />

Indeed, passing callback functions as props to other components is a major step in designing interactive React applications. See the next chapter for more details and examples.

Props and Composition

Recall that React components are usually composed, with one component’s returned value including instances of other components (as in the MessageList example above). Since props are specified when a component instance is created, this means that a “parent” component will need to specify the props for its children (“passing the props”). Commonly, the props for the child components will be derived from the props of the parent component:

//Defines a component representing a message in a list
function MessageItem(props) {
    return <li>{props.message}</li>
}

//A component that renders a trio of messages
function MessageListTrio(props) {
    const messages = props.messages; //assign prop to a local variable for convenience

    return (
        <div>
            <h1>Messages for you</h1>
            <ul>
                {/* instantiate child components, passing data from own props */}
                <MessageItem message={messages[0]} />
                <MessageItem message={messages[1]} />
                <MessageItem message={messages[2]} />
            </ul>
        </div>
    );
}

//Define and pass a prop to the parent comment when rendering it
const messagesArray = ["Hello world", "No borders", "Go huskies!"];
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<MessageListTrio messages={messagesArray});

This creates a one-directional data flow—the data values are passed into the parent, which then passes data down to the children (not vice versa).

The component organization in the previous example is particularly common in React. In order to effectively render a list (array) of data, you should define a Component that declares how to render a single element of that list (e.g., MessageItem). This allows you to focus on only a single problem, developing a re-usable solution. Then you define another component that represents how to render a whole list of elements (e.g., MessageList). This component will render a child item component for each element—it just needs to set up the list, without worrying about what each item looks like!

The data set can be passed into the parent (List) component as a prop, and each individual element from that data set will be passed into an instance of the child (Item) component. In effect, the List component is tasked with taking the data set (an array of data values) and producing a set Item components (an array of Components). This kind of transformation is a mapping operation (each data value is mapped to a Component), and so can be done effecttively with the map() method:

//Map an array of message strings to an array of components
const msgItems = props.messages.map((msgString) => {
    const component = <MessageItem message={msgString} />; //pass prop down!
    return component; //add this new component to resulting array
})

And because including an array of Components as an inline expression in JSX will render those elements as siblings, it’s easy to render this list from within the parent:

function MessageList(props) {
  //the data: an array of strings ["", ""]
  const messageStringArray = props.messages;

  //the renderable content: an array of elements [<>, <>]
  const msgElemArray = messageStringArray.map((msgString) => {
    const component = <MessageItem message={msgString} />; //pass prop down!
    return component; //add this new component to resulting array
  })

  return (
    <div>
      <h1>Messages for you</h1>
      <ul>
        {/* render the array of MessageList components */}
        {msgElemArray}
      </ul>
    </div>
  );
}

//Define and pass a prop to the parent comment when rendering it
const messagesArray = ["Hello world", "No borders", "Go huskies!"];
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<MessageList messages={messagesArray});

Note that the above example will issue a warning in the developer console. This is because React requires you to specify an additional prop called key for each element in an array of components. The key should be a unique string for each element in the list, and acts as an identifier for that element (think: what you might give it as an id attribute). React will use the key to keep track of those elements, so if they change over time (i.e., elements are added or removed from the array) React will be able to more efficiently modify the DOM.

If unspecified, the key will default to the element’s index in the array, but this is considered unstable (since it will change as items are added or removed). Thus a better approach is to determine a unique value based on the data—which means that data items will need to be uniquely identifiable. The below improved version of the code changes each message from a basic String to an Object that has content and id properties, allowing each data value to be uniquely identified. Note that the MessageItem component need not be changed; it still renders an <li> showing the given message prop!

const MESSAGE_DATA = [
    {content: "Hello world", id: 1}
    {content: "No borders", id: 2}
    {content: "Go huskies!", id: 3}
];

function MessageList(props) {
  //the data: an array of objects [{}, {}]
  const messageObjArray = props.messages;

  //the renderable content: an array of elements [<>, <>]
  const msgElemArray = messageObjArray.map((msgObject) => {
    //return a new MessageItem for each message object
    //attributes are listed on their own lines for readability
    return <MessageItem
              message={msgObject.content}
              key={msgObject.id.toString()} {/* pass in a key prop! */}
              />;
  }) //end of .map()

  return (
    <div>
      <h1>Messages for you</h1>
      <ul>
        {msgElemArray}
      </ul>
    </div>
  ); //end of return
}

//Define and pass a prop to the parent comment when rendering it
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<MessageList messages={MESSAGE_DATA});

This pattern enables you to effectively define your web page’s content in terms of individual components that can be composed together, allowing you to quickly render large amounts of data (without needing spend a lot of time thinking about loops!).

Resources

As mentioned above, there are a large number of different resources for learning React. Below are links to key parts of the official documentation.