2022-04-16T14:22:32Z

The React Mega-Tutorial, Chapter 2: Hello, React!

In this chapter you will take your first steps as a React developer. When you reach the end you will have a first version of a microblogging application running on your computer!

The complete course, including videos for every chapter is available to order from my courses site. Ebook and paperback versions of this course are also available from Amazon. Thank you for your support!

For your reference, here is the complete list of articles in this series:

Installing Node.js

While React is a front end framework that runs in the browser, some related utilities are designed to run on your own computer, using the Node.js engine. The project that creates a brand-new React project, for example, runs on Node.

If you don't have a recent version of Node installed, head over to the Node.js download page to obtain it. My recommendation is that you use the latest available LTS (long term support) version.

You can confirm that Node.js is properly installed on your system by opening a terminal and running the following command to check what version you have installed:

node -v

Creating a Starter React Project

There are many ways to create a new React application. The React documentation recommends the Create React App utility when you are creating a brand new single-page application.

Open a terminal window, find a suitable parent directory, and then enter the following command to create a new React project called react-microblog:

npx create-react-app react-microblog

The npx command comes with Node.js, along with npm which you may be more familiar with. Its purpose is to execute Node.js packages. The first argument to npx is the package to execute, which is called create-react-app. Additional arguments are passed to this package once it runs. The Create React App package takes the name of the React project to create as an argument.

You may have noticed that you did not need to install create-react-app prior to running it. The nice thing about npx is that it downloads and runs the requested package on the fly.

Let's have a look at the newly created project. First change into the react-microblog directory:

cd react-microblog

Get a directory listing (ls on Unix and Mac, dir on Windows) to familiarize yourself with your new project.

Depending on the version of Create React App that you use the contents of the project may not match mine exactly, but you should expect to have the following files and directories:

  • README.md: a short document with instructions on how to use your React project.
  • package.json and package-lock.json: the standard Node.js project metadata files, with a description of your project and its dependencies.
  • public: the directory from where the React application will be served during development. Inside this directory you will find the index.html page, which loads the application in the browser, some icon files and other miscellaneous static files.
  • src: the source code directory for the application. This is where you will do your coding. The starter application comes with a few source files for a simple demo application.
  • node_modules: the standard directory where Node.js installs all the dependencies of the project.
  • build: this is not a directory that appears on a freshly created project, but it will be added later, when you create a production build of your project.
  • .git and .gitignore: git repository files. This isn't obvious at first glance because these are hidden, but Create React App also makes the application a local git repository.

Before you continue, you'd want to make sure that the starter project works well. To start the React development web server, run the following command:

npm start

This command runs an initial development build, and creates a web server that serves the React application at the http://localhost:3000 URL. It also opens this URL in your default web browser. Figure 2.1 shows what you should see in your browser.

Figure 2.1: React starter application

Once the application is up and running in the browser, the npm start command enters a source code watch loop. Whenever it detects that changes to source files were made, it automatically rebuilds the application and sends the updated code to the browser. This automated monitoring and refreshing of the application is extremely convenient, so I recommend that you keep the npm start command running at all times while working on the application.

Installing Third-Party Dependencies

The project, as created by the Create React App utility, contains a standard Node.js package.json file that among other things lists all the third-party dependencies used for the project. The initial list of dependencies includes the react and react-scripts libraries, plus a few other related packages.

The application that you are going to build requires a few more packages, so this is a good time to get them installed. Run the following command from the top-level directory of the project:

npm install bootstrap react-bootstrap react-router-dom serve

What are all these packages? Here is a brief summary:

  • bootstrap: a CSS user interface library for web pages.
  • react-bootstrap: a React component library wrapper for the bootstrap package.
  • react-router-dom: a React component library that implements client-side routing.
  • serve: a static file web server that can be used to run the production version of the React application.

You will become familiar with these packages as you work on the project.

Application Structure

On the one side, generating a starter application with Create React App saves a lot of time, but on the other you end up with an application that has some unnecessary components that need to be removed before embarking on a new project. In this section you will learn about the general structure of the React application you just created, and while you are at it, you will remove some cruft.

Before you continue, make sure that you have the npm start command running in a terminal window, and a tab in your browser open on the application. Some of the changes you are going to make soon will cause the application to temporarily break, so this is a great opportunity for you to experience the way the React development server watches your work and notifies you of errors.

The index.html File

If you have worked on other web development projects, you probably know that at the core of a web application that is loaded by the browser there is an HTML file. Once the HTML file is parsed, the browser finds all the references to additional resources needed to render the page, such as images, fonts, stylesheets and JavaScript code, and loads those as well.

The index.html stored in the public directory is the main HTML page for the React application, or to be more accurate, it is a template from which the main HTML page is generated by the React build.

Since React is a single-page application library, this is the only page that the browser will load. Once the page and all its referenced resources are downloaded by the browser, state changes to the application will be managed strictly through JavaScript events, always in the context of this page.

Open public/index.html in your editor and look through it. In the <head> section of the page, you will find some boilerplate that configures icons, character sets and other important page metadata, all very conveniently generated by Create React App.

Somewhere in this section you will find the page's meta description tag, which is used by search engines to show some information about your website in search results. This is the meta description tag generated by Create React App:

    <meta
      name="description"
      content="Web site created using create-react-app"
    />

The first change you are going to make is to update this description to something that is less generic. You are welcome to be creative and write your own description, but here is an example:

public/index.html: Updated meta description tag

    <meta
      name="description"
      content="Microblogging application featured in the React Mega-Tutorial"
    />

A few lines below the meta description tag, you will find the page title:

    <title>React App</title>

The browser tab in which you are running the application displays the React favicon, followed by this title.

Change the page title to "Microblog", as shown below:

public/index.html: Updated page title

    <title>Microblog</title>

Save the index.html and then watch the application in the browser. A second or two after you save, the title in the browser tab will update.

There are no more changes needed in the index.html file, but before you close this file in your editor, scroll down to the <body> section. In this section you are going to find the root element of the application, which looks like this:

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

When the React application starts, it will insert the contents of the page inside this element. In general, you will not need to interact with this <div>, but it is good to understand how the React application ends up in the page.

The manifest.json File

In addition to index.html and the icons, the public directory has a file called manifest.json. This file provides information about the application in JSON format. Client devices such as smartphones and tablets use the information in this file to provide a better experience when the user installs (or creates a shortcut to) the application in their desktop or home screen.

The short_name and name keys are set to generic values, similar to those in the index page. You can update these to "Microblog" and "React Microblog" respectively:

public/manifest.json: Updated application metadata

  "short_name": "Microblog",
  "name": "React Microblog",

The rest of the file defines icons of various sizes, and theme colors for the application. You can leave these settings as they are.

The Icon Files

The favicon and some larger icons of various dimensions used for desktop and mobile shortcuts are all stored in the public directory. If you have the inclination, you can edit or replace these files with your favorite icon design, but this can be done at any time, so it is also fine to leave the files alone for now.

The index.js File

Moving on to the src directory, the index.js file is the main JavaScript file that is loaded by index.html. The task of this file is to bootstrap the React application.

The code in index.js has the entry point for the React rendering engine. The code in this file may vary depending on what version of React and Create React App you use, but in general you should expect to have a main section with a call to render the application, similar to the following:

root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
);

You may notice that there are some strange things in this source file. First, JSX is used in the argument to the render() function. Also, a file with extension .css is being imported at the top, which does not make a lot of sense in terms of the JavaScript language. Keep in mind that all source files in a React project go through a conversion step before they reach the browser, where any extensions to the JavaScript language are processed and converted into valid code.

What does importing a file with a .css extension achieve? One of the main functions of this source code conversion process is to generate optimized JavaScript and CSS bundles that are then downloaded by the browser. Importing a CSS file does not change the JavaScript source code at all, but it informs the build that the referenced CSS file should be added to the application's CSS bundle.

The purpose of the render() function is to generate the contents of the application and apply them to the "root" node from the index.html file. The argument to the render function is a JSX tree representing the entire application.

The App symbol is imported from the App.js source file. This is the top-level component of the application. You will learn more about components shortly, but for now, consider that App represents a hierarchical collection of elements that represent the entire application. A React component is considered a super-powered HTML element, and for that reason it is rendered using angle brackets, such as a standard HTML element. When looking at a JSX tree, you can tell the difference between native HTML elements and React components because the former use lowercase letters and the latter use CamelCase.

What is the <React.StrictMode> component that wraps <App />? This is a component that is part of the React library. It does not render anything visible to the page, but enables some internal checks during development that alert you of possible problems with your code.

At the bottom of index.js you may see a call to a reportWebVitals() function. You are not going to use this in this project, but in case you are interested, generating Web Vitals metrics is an optional feature of React that allows you to analyze the performance of your application.

There is only one change that you are going to make in index.js. As you recall, earlier you installed a few third-party packages. One of those packages was bootstrap, which is a CSS framework. To add this library to the project, its CSS file must be imported. Insert the following import statement right above the line in which the index.css file is imported:

src/index.js: Add the bootstrap framework

import 'bootstrap/dist/css/bootstrap.min.css';

The reason why bootstrap.min.css is imported before index.css is that this gives the application the option to redefine or override the styles that come with the library.

As soon as you save the file, you will notice that the look of the application in the browser changes slightly. This is because the bootstrap styles introduce some minor visual differences on the rendered page.

Deleting Unnecessary Files

The src directory has two files called logo.svg and App.css that are not going to be used by the application, so you are going to delete them.

Why are these files unnecessary? The logo.svg file is the rotating React logo that appears in the center of the page in the starter application. This is a nice graphic for the demo application, but it has no place in your own application. The App.css file stores CSS definitions for the top-level application component, but the project also has an index.css file with application-wide styles, so there is some redundancy in having two CSS files. For this project I have decided to maintain the entire collection of CSS styles in the index.css file.

Since the application was created as a git repository, the preferred way to delete these files is to use git rm from a terminal window, as shown below:

git rm src/logo.svg src/App.css

Right after you delete these two files the npm start process is going to get upset and spew a few error messages on your terminal and on the browser. This is because the App.js source file references the two files that are now gone.

In spite of the errors, npm start will continue to monitor your files and wait for you to fix the errors to restart the application.

A Basic React Application

The App.js file in the src directory is currently broken, as it references files that don't exist anymore in the project. In this section you are going to replace the code in this file with a simpler base application that does not require any external files.

Open src/App.js in your favorite editor or IDE, delete all of its contents and replace them with the following code:

src/App.js: Basic application component

export default function App() {
  return (
    <h1>Microblog</h1>
  );
}

As soon as you save the new version of App.js, the application will automatically reload in your browser and display the <h1> heading in the top-left corner of the page, as shown in Figure 2.2.

Figure 2.2: Basic application

In React, components can be written as classes or as functions. Function-based components are newer and use a more concise syntax, so that is what you'll learn. Functional components were introduced in React 16.8 and are associated with another important feature from that release called hooks. You will learn about hooks in future chapters.

You now know that a React component is implemented as a JavaScript function. The name of the component is the name of the function, in this case App. Component names must begin with a capital letter, and in general are written in CamelCase.

The top-level component of an application is often given the name App, but this is just a convention, not a rule. Component functions need to be exported, so that they can be imported and used from other files. For that reason, component functions are always defined with export default function ....

To keep the source code well organized, a component is written in a source file of the same name, so for example, the App component is written in a source file named App.js. file.

Now on to the most important question. What is a component function supposed to do? In its simplest form, a component must return a representation of itself as an HTML element tree. The function is said to be the component's render function for that reason. The App component above renders itself as an <h1> element with the application's name in it. When the application runs in the browser, this <h1> element is inserted as a child of the root <div> element defined in the application's index.html file.

Note the parenthesis that enclose the JSX that the function returns. Due to the strange and somewhat unpredictable semicolon rules in JavaScript, the opening parenthesis needs to be in the same line as the return keyword, to prevent the JavaScript compiler from inserting a virtual semicolon on that line. The opening parenthesis tells the compiler that the expression continues in the next line.

Dynamic Rendering

Rendering chunks of HTML returned by component functions is nice, but insufficient for the vast majority of applications you may want to build. A key feature that most applications need is the ability to render content that is dynamic. For example, the Microblog application you are going to build needs to render blog posts that are not known in advance, and will be retrieved from a back end service.

The JSX syntax can be expanded with templating expressions, which make it possible to render content stored in variables that can be single values or lists, and it is even possible to define conditional rendering rules.

Rendering Variables

A JSX definition can include JavaScript expressions, given inside curly brackets. For example, if a variable name is set to the string 'susan', the JSX expression <h1>Hello, {name}!</h1> would render <h1>Hello, susan!</h1> to the page. This not only works for text, but also for attributes of elements, so for example, <img src={image_url} /> would render an image that references the URL stored in the image_url variable.

This application is going to render blog posts, but these will be obtained from a back end, but at this early stage none of that is available. To avoid getting a stuck with too many problems that need a solution, a useful technique is to mock parts of the application that aren't ready yet.

Given that there is no back end yet, let's create a mock blog post that can be rendered to the page. Replace the code in App.js with the following:

src/App.js: Render a blog post

export default function App() {
  const post = {
    id: 1,
    text: 'Hello, world!',
    timestamp: 'a minute ago',
    author: {
      username: 'susan',
    },
  }

  return (
    <>
      <h1>Microblog</h1>
      <p>
        <b>{post.author.username}</b> &mdash; {post.timestamp}
        <br />
        {post.text}
      </p>
    </>
  );
}

The post constant defined at the start of the App() function is a fake blog post that can be used to implement the code that renders posts to the page, without having to implement the connection to a back end first, something that is a much more complex task.

The return statement renders the same <h1> element as before, followed by a <p> element that includes the author's username, the timestamp and the text of the blog post, with minimal HTML formatting. Text that is included directly in the JSX block is rendered verbatim to the page, while text that is inside { } is evaluated as a JavaScript expression, and the result is what is rendered to the page.

In the previous version of this code, a single <h1> element was returned, but in this version there's a <p> as well. React requires that the render tree returned by a component is a proper tree, with a single top-level node. It would be easy to add a parent <div>, but that will render an unnecessary element to the page. Using empty tags <> and </> is more efficient, as these do not produce any render output. These tags create a fragment, which is an invisible parent that allows the grouping of multiple nodes into a single tree.

The <br /> notation might also look strange if you are used to often see <br> to insert line breaks in HTML pages. JSX requires a strict XML syntax, so all elements must be properly closed.

Figure 2.3 shows how the blog post looks on the browser.

Figure 2.3: Render a blog post

Rendering Lists of Elements

The techniques shown in the previous section can be used with variables or constants that are assigned simple values or objects. But what happens if you need to render an array?

When the expression inside curly brackets is an array, React outputs the elements of the array one after another. Consider the following example:

export default function RenderArray() {
  const data = ['one', 'two', 'three'];

  return (
    <>{data}</>
  );
}

The output of this component is going to be onetwothree, which isn't very useful, as there is no separation between the items and no way to add HTML markup for each element of the array.

The way to make this work is to use the map() method of the Array class to transform each element into the desired JSX expression. The map() method takes a function as an argument. This function is called once for each of the elements in the array, with the element as its only argument. The return values for all these calls are collected and returned by map() as a new array, and this becomes the render output of the component.

To render the above list as an HTML unordered list, you could rewrite the component as follows:

export default function RenderArray() {
  const data = ['one', 'two', 'three'];

  return (
    <ul>
      {data.map(element => {
        return <li>{element}</li>
      })}
    </ul>
  );
}

If you aren't familiar with the Array.map() method, the inner return statement in this last example may seem strange. Remember that map() takes a function as its argument, so this inner return is returning values back to map(), which is the caller of the inner function.

With the map() method, the output of the component becomes a proper HTML list:

<ul><li>one</li><li>two</li><li>three</li></ul>

This technique can be applied to Microblog. In the previous section, the application rendered a single post. That can be extended to work with a list of posts. Replace the code in src/App.js with the following:

src/App.js: Render a list of blog posts

export default function App() {
  const posts = [
    {
      id: 1,
      text: 'Hello, world!',
      timestamp: 'a minute ago',
      author: {
        username: 'susan',
      },
    },
    {
      id: 2,
      text: 'Second post',
      timestamp: 'an hour ago',
      author: {
        username: 'john',
      },
    },
  ];

  return (
    <>
      <h1>Microblog</h1>
      {posts.map(post => {
        return (
          <p>
            <b>{post.author.username}</b> &mdash; {post.timestamp}
            <br />
            {post.text}
          </p>
        );
      })}
    </>
  );
}

You can see how the list of posts looks in the browser in Figure 2.4.

Figure 2.4: Render a list of blog posts

This looks great, but there is a hidden problem. If you look at the browser's debugging console, you will see a warning message.

Warning: Each child in a list should have a unique "key" prop.

Check the render method of `App`.

React has a performance optimization that triggers when a list that is rendered by a component changes. The goal is to update lists efficiently, by only updating the elements that were added, removed or changed. To be able to determine which elements of a list need to be updated, React requires each list element to be given a unique key attribute. When all elements have a key, React can compare the current and new versions of the list and determine which elements are new, removed or updated. More importantly, it allows React to know which of the elements have not changed at all, so it can optimize the render process by reusing those items from the previous page update.

When running in development mode, React triggers this warning when it finds a list that is rendered without keys. To remove the warning, a key attribute needs to added to the top-level JSX node rendered for each element. Depending on the elements, you will need to find a unique value that can serve as identifier. Objects that are retrieved from a server back end often have an id attribute that works perfectly as keys.

Below is the posts loop, modified to use the id attributes defined in the fake blog posts as keys.

src/App.js: Render a list of blog posts with unique keys

      {posts.map(post => {
        return (
          <p key={post.id}>
            ...  // <-- no changes to the post JSX
          </p>
        );
      })}

Conditional Rendering

The last templating trick you are going to learn gives the React application the ability to render parts of the JSX tree only when certain condition is true.

Revisiting the RenderArray example component from the previous section, see how it can be extended to show a message when the array has no elements to render:

export default function RenderArray() {
  const data = [];

  return (
    <>
      <ul>{data.map(element => <li>{element}</li>)}</ul>
      {data.length === 0 &&
        <p>There is nothing to show here.</p>
      }
    </>
  );
}

Here the "and" operator is used to create an expression that will only include the JSX contents on the right side of the && if the condition on its left is true.

The above example is not perfect, because when the list is empty an empty <ul> element is rendered to the page before the error message. The && operator maps nicely to an if-then construct, but in this situation it would be ideal to be able to use an if-then-else, which can be implemented similarly, but using the ternary conditional operator (?:).

export default function RenderArray() {
  const data = [];

  return (
    <>
      {data.length === 0 ?
        <p>There is nothing to show here.</p>
      :
        <ul>{data.map(element => <li>{element}</li>)}</ul>
      }
    </>
  );
}

The same idea can be implemented in the Microblog application. Replace the contents of src/App.js with the code below.

src/App.js: Render a list of blog posts with empty warning

export default function App() {
  const posts = [
    {
      id: 1,
      text: 'Hello, world!',
      timestamp: 'a minute ago',
      author: {
        username: 'susan',
      },
    },
    {
      id: 2,
      text: 'Second post',
      timestamp: 'an hour ago',
      author: {
        username: 'john',
      },
    },
  ];

  return (
    <>
      <h1>Microblog</h1>
      {posts.length === 0 ?
        <p>There are no blog posts.</p>
      :
        posts.map(post => {
          return (
            <p key={post.id}>
              <b>{post.author.username}</b> &mdash; {post.timestamp}
              <br />
              {post.text}
            </p>
          );
        })
      }
    </>
  );
}

With this version of the application you can comment out the posts constant and put an empty array in its place, and the page will automatically change to show the message in the "else" part, as seen in Figure 2.5.

Figure 2.5: Render a warning that the post list is empty

Once you confirm that the conditional logic is working, remember to restore the fake blog posts.

Chapter Summary

  • To start a new React project easily, use Create React App (CRA).
  • Remove the cruft added by CRA before you begin coding your application.
  • A React component is a JavaScript function that renders a JSX tree and returns it.
  • Insert JavaScript expressions in your JSX by enclosing them in curly brackets.
  • Render lists of elements with map(), and include a key attribute with a unique value per element.
  • Add conditional rendering expressions with the && (if-then) and ?: (if-then-else) operators.

Ready to continue? Here is Chapter 3!

9 comments

  • #1 Alex said 2022-04-18T00:28:35Z

    I found for your "RenderArray()" examples I had to include the keyword "function" in front of "RenderArray()" to make the page load as described.

    Making the first line "export default function RenderArray() {" instead of "export default RenderArray() {".

    I believe I have followed your instructions for libraries to build with, so not sure if I missed an update or something.

  • #2 Miguel Grinberg said 2022-04-18T09:57:12Z

    @Alex: thanks, I have corrected those examples to include the missing function keyword.

  • #3 Duarte said 2022-05-06T15:34:06Z

    Miguel - your tutorials are the absolute best. It's like you are in my brain ahahaha!!! Muchas Gracias!

  • #4 colin said 2022-06-10T15:06:40Z

    Amazing tutorial! I'm about to buy the course but I'm wondering if a Kindle version of the book is included with the video course, or is it just a PFD?

  • #5 Miguel Grinberg said 2022-06-10T16:15:03Z

    @colin: the ebook comes in PDF, EPUB and MOBI. The MOBI version should load nicely into your Kindle.

  • #6 Sohbet Gurbanov said 2022-06-15T08:45:56Z

    Hey Miguel, i ended your Flask Mega Tutorial, thank you very much.

    npx create-react-app react-microblog, i typed this to my terminal and waited 15 minutes it's not showed anything( . It's not working. It's not showed any error

  • #7 Miguel Grinberg said 2022-06-15T11:13:07Z

    @Sohbet: You may want to reinstall Node.js. And make sure it is a recent version.

  • #8 Ben said 2022-07-05T08:04:33Z

    Hi Miguel,

    first of all, thanks very much for the flask tutorial, it kick started my career!

    Secondly, should I be worried about the 94 vulnerabilities associated with the bootstrap, react-bootstrap, react-router-dom, and serve packages. I am assuming yes and have fixed most of them however, 6 high severity vulnerabilities remain, seemingly associated with the nth-check dependency. Upon further research (stack overflow) it seems like this is not an issue but are these warnings normal and when should I be worried?

    Thirdly....do you have a spanish language version of the book on the way?

    Thanks!

  • #9 Miguel Grinberg said 2022-07-05T16:29:20Z

    @Ben: What are these vulnerabilities and who is reporting them? I'm not planning to translate the book to Spanish, sorry.

Leave a Comment