React Cookbook

Recipes for Mastering the React Framework

With Early Release ebooks, you get books in their earliest form — the authors’ raw and unedited content as they write — so you can take advantage of these technologies long before the official release of these titles.

David Griffiths and Dawn Griffiths

React Cookbook

by David Griffiths and Dawn Griffiths

Copyright © 2021 O’Reilly Media. All rights reserved.

Printed in the United States of America.

Published by O’Reilly Media, Inc., 1005 Gravenstein Highway North, Sebastopol, CA 95472.

O’Reilly books may be purchased for educational, business, or sales promotional use. Online editions are also available for most titles (http://oreilly.com). For more information, contact our corporate/institutional sales department: 800-998-9938 or corporate@oreilly.com.

- Acquisitions Editor: Amanda Quinn

- Development Editor: Corbin Collins

- Production Editor: Daniel Elfanbaum

- Interior Designer: David Futato

- Cover Designer: Karen Montgomery

- Illustrator: O’Reilly Media, Inc.

- October 2021: First Edition

Revision History for the Early Release

- 2020-08-05: First Release

See http://oreilly.com/catalog/errata.csp?isbn=9781492085843 for release details.

The O’Reilly logo is a registered trademark of O’Reilly Media, Inc. React Cookbook, the cover image, and related trade dress are trademarks of O’Reilly Media, Inc.

The views expressed in this work are those of the authors, and do not represent the publisher’s views. While the publisher and the authors have used good faith efforts to ensure that the information and instructions contained in this work are accurate, the publisher and the authors disclaim all responsibility for errors or omissions, including without limitation responsibility for damages resulting from the use of or reliance on this work. Use of the information and instructions contained in this work is at your own risk. If any code samples or other technology this work contains or describes is subject to open source licenses or the intellectual property rights of others, it is your responsibility to ensure that your use thereof complies with such licenses and/or rights.

978-1-492-08577-5

[LSI]

Preface

The following software versions have been used:

Tool/library

Description

Versions

Create React App

Tool for generating React apps

3.4.1

Cypress

Automated test system

4.11.0

Gatsby

Tool for generating React apps

2.23.12

Material-UI

Component library

4.11.0

Node

JavaScript runtime

v10.17.0

NPM

The Node package manager

6.11.3

NVM

Tool for running multiple Node environments

0.33.2

NWB

Tool for generating React apps

0.25.x

Next.js

Tool for generating React apps

9.5.1

Preact

Lightweight React-like framework

10.3.2

Preact Custom Elements

Library to create custom elements

3.0.0

Rails

Web development framework

6.0.3.2

Razzle

Tool for generating React apps

3.1.6

React

Web framework

16.13.1

React Media

Media queries in React code

1.10.0

React Router (DOM)

Library for managing React routes

5.2.0

React Testing Library

Unit testing library for React

9.3.2

Ruby

Language used by Rails

2.7.0p0

Storybook

Component gallery system

5.3.19

Typescript

Type-safe extension to JavaScript

3.7.2

Webpacker

Tool for adding React to Rails apps

4.2.2

Yarn

Another Node package manager

1.12.3

Conventions Used in This Book

The following typographical conventions are used in this book:

Italic

Indicates new terms, URLs, email addresses, filenames, and file extensions.

Constant width

Used for program listings, as well as within paragraphs to refer to program elements such as variable or function names, databases, data types, environment variables, statements, and keywords.

Constant width bold

Shows commands or other text that should be typed literally by the user.

Constant width italic

Shows text that should be replaced with user-supplied values or by values determined by context.

Tip

This element signifies a tip or suggestion.

Note

This element signifies a general note.

Warning

This element indicates a warning or caution.

Using Code Examples

Supplemental material (code examples, exercises, etc.) is available for download at TBD.

If you have a technical question or a problem using the code examples, please send email to bookquestions@oreilly.com.

This book is here to help you get your job done. In general, if example code is offered with this book, you may use it in your programs and documentation. You do not need to contact us for permission unless you’re reproducing a significant portion of the code. For example, writing a program that uses several chunks of code from this book does not require permission. Selling or distributing examples from O’Reilly books does require permission. Answering a question by citing this book and quoting example code does not require permission. Incorporating a significant amount of example code from this book into your product’s documentation does require permission.

We appreciate, but generally do not require, attribution. An attribution usually includes the title, author, publisher, and ISBN. For example: “React Cookbook by David Griffiths and Dawn Griffiths (O’Reilly). Copyright 2021 O’Reilly Media Inc., 978-1-492-08584-3.”

If you feel your use of code examples falls outside fair use or the permission given above, feel free to contact us at permissions@oreilly.com.

O’Reilly Online Learning

Note

For more than 40 years, O’Reilly Media has provided technology and business training, knowledge, and insight to help companies succeed.

Our unique network of experts and innovators share their knowledge and expertise through books, articles, and our online learning platform. O’Reilly’s online learning platform gives you on-demand access to live training courses, in-depth learning paths, interactive coding environments, and a vast collection of text and video from O’Reilly and 200+ other publishers. For more information, visit http://oreilly.com.

How to Contact Us

Please address comments and questions concerning this book to the publisher:

- O’Reilly Media, Inc.

- 1005 Gravenstein Highway North

- Sebastopol, CA 95472

- 800-998-9938 (in the United States or Canada)

- 707-829-0515 (international or local)

- 707-829-0104 (fax)

We have a web page for this book, where we list errata, examples, and any additional information. You can access this page at http://www.oreilly.com/catalog/9781492085843.

Email bookquestions@oreilly.com to comment or ask technical questions about this book.

For news and information about our books and courses, visit http://oreilly.com.

Find us on Facebook: http://facebook.com/oreilly

Follow us on Twitter: http://twitter.com/oreillymedia

Watch us on YouTube: http://www.youtube.com/oreillymedia

Chapter 1. Creating Applications

A note for Early Release readers

With Early Release ebooks, you get books in their earliest form — the authors’ raw and unedited content as they write — so you can take advantage of these technologies long before the official release of these titles.

This will be the 1st chapter of the final book. Please note that the GitHub repo will be made active later on.

If you have comments about how we might improve the content and/or examples in this book, or if you notice missing material within this chapter, please reach out to the editor at ccollins@oreilly.com.

React is a surprisingly adaptable development framework. Developers use it to create large JavaScript-heavy Single Page Applications or to build surprisingly small plug-ins. You can use it to embed code inside a Rails applications or generate a content-rich web site.

In this chapter, we look at the various ways of creating a React application. We also look at some of the more useful tools that you might want to add to your development cycle. Very few people now create their JavaScript projects from scratch. Doing so is now a very tedious process, involving an uncomfortable amount of tinkering and configuration. The good news is that in almost every case, you can use a tool to generate the code you need.

So let’s take a whistle-stop tour of the many ways of starting your React journey, beginning with the one most frequently used: create-react-app….

1.1 Create a Vanilla App with create-react-app

Problem

React projects are difficult to create and configure from scratch. Not only are there numerous design choices to make–which libraries to include, which tools to use, which language features to enable–but manually created applications will, by their nature, differ from one another. Project idiosyncrasies increase the time it takes a new developer to become productive.

Solution

create-react-app is a tool for building SPAs with a standard structure and a reasonable set of default options. Generated projects use the react-scripts library to build, test, and run the code. Projects have a standard Webpack configuration and a standard set of language features enabled.

Any developer who has worked on one create-react-app application instantly feels at home with any other. They understand the project structure and know which language features they can use. It is simple to use and contains all the features that a typical application requires: from babel configuration and file loaders to testing libraries and a development server.

If you’re new to React, or need to create a generic SPA with the minimum of fuss, then you should consider creating your app with create-react-app.

You can choose to install the create-react-app command globally on your machine, but this is now discouraged. Instead, you should create a new project by calling create-react-app via npx. Using npx ensures you’re building your application with the latest version of create-react-app:

npx create-react-app my-app

This command creates a new project directory called my-app. By default, the application uses JavaScript. If you want to use TypeScript as your development language, create-react-app provides that as an option:

npx create-react-app --typescript my-app

Facebook developed create-react-app, so it should come as no surprise that your new project uses the yarn package manager. To use npm, change into the directory and remove the yarn.lock file, and then re-run the install with npm:

cd my-app
rm yarn.lock
npm install

To start your application, you should run the start script:

npm run start # or yarn start

This command launches a server on port 3000, and opens a browser at the home page:

Figure 1-1. The generated front page

The server delivers your application as a single, large bundle of JavaScript. The code mounts all of its components inside this <div/> in public/index.html:

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

The code to generate the components begins in the src/index.js file (src/index.tsx if you’re using TypeScript):

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';

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

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();

This file does little more than render a single component called <App/>, which is imported from App.js in the same directory:

import React from 'react';
import logo from './logo.svg';
import './App.css';

function App() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
</div>
);
}

export default App;

If you edit this file while the application is start-ed, the page in the browser automatically updates.

When you’re ready to ship the code to production, you need to generate a set of static files that you can deploy on a standard web server. To do this, run the build script:

npm run build

The build script creates a build/ directory and in there generates a set of static files:

├── asset-manifest.json
├── favicon.ico
├── index.html
├── logo192.png
├── logo512.png
├── manifest.json
├── precache-manifest.04a2f4deb0e98d3c8eb66908edce9f33.js
├── robots.txt
├── service-worker.js
└── static
....├── css
....├── js
....└── media

The build copies many of these files from the public/ directory. The code for the app is transpiled into browser-compatible JavaScript and stored in one or more files in the static/js directory. Stylesheets used by the application, are stitched together and stored in static/css. Several of the files have randomized ids added to them so that when you deploy your application, browsers download the latest code, rather than some old cached version.

Discussion

create-react-app is not just a tool for generating a new application, but also a platform to keep your React application up-to-date with the latest tools and libraries. You can upgrade the react-scripts library as you would any other: by changing the version number and re-running npm install. You don’t need to manage a list of babel plug-ins, postcss libraries, or maintain a complex webpack.config.js file. The react-scripts library manages them all for you.

If, however, you later decide to manage all of this yourself, you’re free to do so. If you eject the application, then everything comes back under your control:

npm run eject

However, this is a one-time-only change. Once you have ejected your application, there is no going back. You should think carefully before ever ejecting an application. You may find that the configuration you need is already available. For example, developers would often eject an application to switch to using TypeScript. The --typescript flag now removes the need for that.

Another common reason for ejecting was to proxy web services. React apps often need to connect to some separate API backend. Developers used to do this by configuring Webpack to proxy a remote server through the local development server. You can now avoid do this by setting a proxy in the package.json file:

"proxy": "http://myapiserver",

If your code now contacts a URL that the server cannot find locally (/api/thing), the react-scripts automatically proxy these requests to http://myapiserver/api/thing.

Tip

If you possibly can, avoid ejecting your application. Look through the create-react-app documentation at https://create-react-app.dev/ to see if you can make the change some other way.

You can download the source for this recipe in JavaScript or TypeScript from the Github site.

1.2 Build Content-Rich Apps with Gatsby

Problem

Content-rich sites like blogs and online stores need to serve large amounts of complex content efficiently. A tool like create-react-app is not suitable for this kind of web site because it delivers everything as a single large bundle of JavaScript that a browser must download before anything displays.

Solution

If you are building a content-rich site, consider using Gatsby.

Gatsby focuses on loading, transforming, and delivering content in the most efficient way possible. It can generate static versions of web pages, which means that the response times of Gatsby sites are often significantly lower than, say, those built with create-react-app.

Gatsby has a large number of plug-ins that can load and transform data efficiently from static local data, GraphQL sources, and third-party CMS systems such as Wordpress.

You can install gatsby globally, but you can also run it via the npx command:

npx gatsby new my-app

The gatsby new command creates a new project in a subdirectory called my-app. The first time you run this command, it asks which package manager to use: either yarn or npm.

To start your application, change into the new directory and run it in development mode:

cd my-app
npm run develop

Figure 1-2. Gatsby page at http://localhost:8000

Gatsby projects have a straightforward structure:

├── LICENSE
├── README.md
├── gatsby-browser.js
├── gatsby-config.js
├── gatsby-node.js
├── gatsby-ssr.js
├── node_modules/
├── package-lock.json
├── package.json
└── src
....├── components
....├── images
....└── pages

The core of the application lives under the src/ directory. Each page within a Gatsby app has its own React component. This is the front page of the default application:

import React from "react"
import { Link } from "gatsby"

import Layout from "../components/layout"
import Image from "../components/image"
import SEO from "../components/seo"

const IndexPage = () => (
<Layout>
<SEO title="Home" />
<h1>Hi people</h1>
<p>Welcome to your new Gatsby site.</p>
<p>Now go build something great.</p>
<div style={{ maxWidth: `300px`, marginBottom: `1.45rem` }}>
<Image />
</div>
<Link to="/page-2/">Go to page 2</Link> <br />
<Link to="/using-typescript/">Go to "Using TypeScript"</Link>
</Layout>
)

export default IndexPage

There is no need to create a route for the page. Each page component is automatically assigned a route. For example, the page at src/pages/using-typescript.tsx [1] is automatically available at /using-typescript/. This approach has multiple advantages. First, if you have a lot of pages, you don’t need to manage the routes for them manually. Second, it means that Gatsby can deliver much more rapidly. To see why let’s look at how to generate a production build for a Gatsby application.

If you stop the Gatsby development server[2], you can generate a production build with the following:

npm run build

This command runs a gatsby build command, which creates a public/ directory. And it is the public/ directory that contains the real magic of Gatsby. For each page, you find two files. First, a generated JavaScript file:

1389 06:48 component---src-pages-using-typescript-tsx-93b78cfadc08d7d203c6.js

Here you can see that the code for using-typescript.tsx is just 1389 bytes long and which, with the core framework, is just enough JavaScript to build the page. It is not the kind of include-everything script that you find in a create-react-app project.

Secondly, there is a subdirectory for each page, containing a generated HTML file. For using-typescript.tsx the file is called public/using-typescript/index.html, which is a statically generated version of the web page. It contains the HTML that the using-typescript.tsx component would otherwise render dynamically. At the end of the web page, it loads the JavaScript version of the page in case it needs to generate some dynamic content.

This file structure means that Gatsby pages load in around the same time that it takes to load a static web page. Using the bundled react-helmet library, you can also generate <meta/> header tags with additional features about your site. Both features are great for Search Engine Optimization.

Discussion

How will the content get into your Gatsby application? You might use a headless CMS system, a GraphQL service, a static data source, or something else. Fortunately, Gatsby has many plug-ins which allow you to connect data sources to your application, and then transform the content from a format such as Markdown into HTML.

You can find a full set of plug-ins on the Gatsby web site.

Most of the time, you choose the plug-ins you need when you first create the project. To give you a head-start, Gatsby also supports start templates. The template provides the initial application structure and configuration. The app we built above uses the default started template, which is quite simple. The gatsby-config.js file in the root of the application configures which plug-ins your application uses.

But there are masses of Gatsby starters available, pre-configured to build applications that connect to a variety of data sources, with pre-configured options for SEO, styling, offline caching, PWA (Progressive Web Applications), and more. Whatever kind of content-rich application you are building, there is a starter that is close to what you need.

There is more information on the Gatsby web site about Gatsby starters, as well as a cheat sheet for the most useful tools and commands.

You can download the source for this recipe from the Github site.

1.3 Build Universal Apps with Razzle

Problem

Sometimes when you start to build an application, it is not always clear what the main architectural decisions will be. Should you create a SPA? If performance is critical, should you use Server Side Rendering? You will need to decide what your deployment platform will be, and whether you are going to write your code in JavaScript or TypeScript.

Many tools require that you answer these questions early on. If you later change your mind, modifying the way you build and deploy your application can be complicated.

Solution

If you want to defer decisions about how you build and deploy your application, you should consider using Razzle.

Razzle is a tool for building Universal applications: that is, applications that can execute their JavaScript on the server. Or the client. Or both.

Razzle uses a plug-in architecture that allows you to change your mind about how you build your application. It will even let you change your mind about whether you are building your code in React, or Preact or some other framework entirely, like Elm or Vue.

You can create a Razzle application with the create-razzle-app command[3]:

npx create-razzle-app my-app

This command creates a new Razzle project in the my-app subdirectory. You can start the development server with the start script:

cd my-app
npm run start

The start script will dynamically build both client code and server code, and then run the server on port 3000, as shown in figure 1-3.

Figure 1-3. The Razzle front page at http://localhost:3000

When you want to deploy a production version of your application, you can then run the build script:

npm run build

Unlike create-react-app, this will build not just the client code, but also a node server. Razzle generates the code in the build/ subdirectory. The server code will continue to generate static code for your client at runtime. You can start a production server by running the build/server.js file with node using the start:prod script:

npm run start:prod

You can deploy the production server anywhere that node is available.

The server and the client can both run the same code, which is what makes it Universal. But how does it do this?

The client and the server have different entry points. The server runs the code in src/server.js; the browser runs the code in src/client.js. Both server.js and client.js then render the same app using src/App.js.

If you want to run your app as a SPA, remove the app/index.js and app/server.js files. Then create an index.html in the public/ folder containing a <div/> with id root, and re-build the application with:

node_modules/.bin/razzle build --type=spa

You will generate a full SPA in build/public/ that you can deploy on any web server.

Discussion

Razzle is so adaptable because it is built from a set of highly configurable plug-ins. Each plug-in is a higher-order function that receives a webpack configuration and returns a modified version. One plug-in might transpile TypeScript code, another might bundle the React libraries.

If you want to switch your application to Vue, you only need to replace the plug-ins you use.

You can find a list of available plug-ins on the Razzle web site.

You can download the source for this recipe from the Github site.

1.4 Manage Server and Client Code with Next.js

Problem

The focus of React is on client code–even if that client code is generated on the server. Sometimes, however, you might have a relatively small amount of API code that you would prefer to manage as part of the same React application.

Solution

Next.js is a tool for generating React applications that include their own server code. The api end-points and the client pages use default routing conventions, which makes them simpler to build and deploy than they would be if you manage them yourself. You can find full details about Next.js on the web site.

At the time of writing, you cannot create a Next.js application using npx. Instead, you should first install create-next-app globally:

npm install -g create-next-app

Then, you can generate a new application:

create-next-app my-app

This will create a Next.js application in the my-app subdirectory. To start the app, run the start script:

cd my-app
npm run start

Figure 1-4. A NextJS page running at http://localhost:3000

Next.js allows you to create pages without the need to manage any routing configuration. If you add a component script to the pages/ folder, it will instantly become available through the server. For example, the pages/index.js component is used to generate the home page of the default application.

This approach is similar to the one taken by Gatsby[4] but is taken further in Next.js, to include server-side code as well.

Next.js applications usually include some API server code. This is unusual for React applications, which are often built quite separately from server code. But if you look inside pages/api you will find an example server end-point called hello.js:

// Next.js API route support: https://nextjs.org/docs/api-routes/introduction

export default (req, res) => {
res.statusCode = 200
res.json({ name: 'John Doe' })
}

The routing which mounts this to the end-point api/hello happens automatically.

The code that you write in Next.js is automatically built into a hidden directory called .next/. This code can then be deployed to a service such as Next.js’ own Vercel platform.

If you want, you generate a static build of your application with:

node_modules/.bin/next export

This will build your client code in a directory called out/. Each page of your site will be converted into a statically rendered HTML file, which will load very quickly in the browser. At the end of the page, it will load the JavaScript version in case the React code needs to modify the DOM.

Warning

If you create an exported version of a Next.js application, it won’t include any server-side APIs.

Next.js comes with a bunch of data-fetching options, which allow you to get data from static content, or via headless CMS sources.

Discussion

Next.js is in many ways similar to Gatsby. Its focus is on the speed of delivery, with a small amount of configuration. It’s probably most useful for teams who will have a small amount of server code that they want to manage simply.

You can download the source for this recipe from the Github site.

1.5 Create a Tiny App with Preact

Problem

React applications can be large. It’s quite easy to create a simple React application which is transpiled into bundles of JavaScript code that are several hundred Kbs in size. There are times when you might want to build an app with React-like features, but without having to download a large amount of JavaScript code.

Solution

If you want React-features, but don’t want to pay the price of a React-size JavaScript bundle, you might want to consider using Preact.

Preact is not React. It is a separate library, but it is designed to be as close to React as possible while being much, much smaller.

The reason that the React framework is so big is because of the way it works. React components don’t generate elements in the Document Object Model (DOM) of the browser directly. Instead, they build elements within a virtual DOM, which is then used to update the actual DOM at frequent intervals. Doing so allows basic DOM-rendering to be fast because the actual DOM only needs to be updated when there are real changes. However, it does have a downside. React’s virtual DOM requires a lot of code to keep it up to date. It needs to manage an entire synthetic event model, which parallels the one in the browser. For this reason, the React framework is large and can take some time to download.

One way around this is to use techniques such as Server-Side Rendering[5], but SSR can be complex to configure. Sometimes, you just want to download a small amount of code. And that’s why Preact exists.

The Preact library, although similar to React, is tiny. At the time of writing, the main Preact library is around 4Kb. This is small enough that it’s possible to add React-like features to web pages in barely more code than is required to write native JavaScript.

Preact lets you choose how to use it: as a small JavaScript library included in a web page (the no tools approach) or as a full-blown JavaScript application.

The no-tools approach is really very basic. The core Preact library does not support JSX, and you will have no Babel support and so you will not be able to use modern JavaScript. This is an example web page using the raw Preact library:

<html>
<head>
<title>No Tools!</title>
<script src="https://unpkg.com/preact?umd"></script>
</head>
<body>
<h1>No Tools Preact App!</h1>
<div id="root"></div>
<script>
var h = window.preact.h;
var render = window.preact.render;

var mount = document.getElementById('root');

render(
h('button',
{
onClick: function() {
render(h('div', null, 'Hello'), mount);
}
},
'Click!'),
mount
);
</script>
</body>
</html>

This application will mount itself at the <div/> with id root, where it will display a button. When you click the button, it will replace the contents of the root div with the string "Hello". This is about as basic as a Preact app can be.

You would rarely write an application in this way. In reality, you would create a simple build-chain that would, at the very least, support modern JavaScript.

In fact, Preact supports the entire spectrum of JavaScript applications. At the other extreme, you can create a full Preact application, with the preact-cli.

preact-cli is a tool for creating Preact projects and is analogous to tools like create-react-app. You can install preact-cli globally like this:

npm install -g preact-cli

Then you can create a Preact application with:

preact create default my-app

This will create your new Preact application in the my-app/ subdirectory. To start it, run the dev script:

cd my-app
npm run dev

This will start the server on port 8080, as shown in figure 1-5.

Figure 1-5. A page from Preact

The server generates a web page, which calls back for a JavaScript bundle made from the code in src/index.js.

You now have a full-scale React-like application. The code inside the Home component, for example, looks very react-like, with full JSX support.

import { h } from 'preact';
import style from './style';

const Home = () => (
<div class={style.home}>
<h1>Home</h1>
<p>This is the Home component.</p>
</div>
);

export default Home;

The only significant difference from a standard React component, is that a function called h is imported from the preact library, instead of importing React from the react library.

However, the size of the application has increased: it is now a little over 300Kb. That’s pretty large, but we are still in dev-mode. To see the real power of Preact, stop the dev server[6] and then run the build script:

npm run build

This will generate a static version of the application in the build/ directory. First of all, this will have the advantage of creating a static copy of the front page, which will render very quickly. Secondly, it will remove all unused code from the application and shrink everything down. If you serve this built version of the app on a standard web server, the browser will transfer only about 50-60Kb when it’s opened.

Discussion

Preact is a remarkable project. Despite working in a very different way to React, it provides virtually the same power, at a fraction of the size. And the fact that it can be used for anything between the lowliest inline code to a full-blown SPA means it is well worth considering if code-size is critical to your project.

You can find out more about Preact on the Preact web site.

You can download the source for the no-tools example and the larger Preact example from the Github site.

If you would to make Preact look even more like React, see the preact-compat library.

Finally, for a project that takes a similar approach to Preact, look at InfernoJS.

1.6 Build Libraries with NWB

Problem

Large organizations often develop several React applications at the same time. If you’re a consultancy, you might create applications for multiple organizations. If you’re a software house, you might create various applications that require the same look and feel, so you will probably want to build shared components that can be used across several applications.

When you create a component project, you need to create a directory structure, select a set of tools, choose a set of language features and create a build chain that can bundle your component in a deployable format. This can be just as tedious as manually creating a project for an entire React application.

Solution

The NWB toolkit can be used to create full React applications, but can also create projects that are specifically intended to create a single React component. In fact, it can also create components for use within Preact and InjernoJS projects, but we shall concentrate on React components here.

To create a new React component project, you will first need to install the nwb tool globally:

npm install -g nwb

You can then create a new project with the nwb command:

nwb new react-component my-component

Note

If instead of creating a single component, you want to create an entire NWB application, you can replace react-component in this command with react-app, preact-app, or inferno-app to create an application in the given framework. You can also use vanilla-app if you want to create a basic JavaScript project without a framework.

When you run this command, you will be asked several questions about the type of library you want to build. You will be asked if you’re going to build ECMAScript modules:

Creating a react-component project...
? Do you want to create an ES modules build? (Y/n)

This will allow you to build a version including an export statement, which can use by WebPack to decide whether or not the module is required in a client application. You will also be asked if you want to create a Universal Module Definition:

? Do you want to create a UMD build? (y/N)

That’s useful if you want to include your component in a <script/> within a web page. For our example, we won’t create a UMD build.

This will create an NWB component project inside the my-component/ subdirectory. The project comes with a simple wrapper application that you can start with the start script:

cd my-component
npm run start

The demo application runs on port 3000, as shown in figure 1-6.

Figure 1-6. An NWB component

The application will contain a single component defined in src/index.js.

import React, {Component} from 'react'

export default class extends Component {
render() {
return <div>
<h2>Welcome to React components</h2>
</div>
}
}

You can now build the component as you would any React project. When you are reading to create a publishable version, simply type:

npm run build

This will now create a built version of your component in lib/index.js, which you can deploy to a repository for use within other projects.

Discussion

For further details on creating NWB components, see the NWB guide to developing components and libraries.

You can download the source for this recipe from the Github site.

1.7 Add React to Rails with Webpacker

Problem

The Rails framework was created before interactive JavaScript applications became popular. Rails applications follow a more traditional model for web application development, in which HTML pages are rendered on the server in response to browser requests. But sometimes you may want to include more interactive elements inside a Rails application.

Solution

The Webpacker library can be used to insert React applications into Rails generated web pages. To see how it works, let’s first generate a Rails application which includes Webpacker:

rails new my-app --webpack=react

This will create a Rails application in a directory called my-app/ that is preconfigured to run a Webpacker server. Before we start the application, let’s go into it and generate an example page/controller:

cd my-app
rails generate controller Example index

That will generate this template page at app/views/example/index.html.erb:

<h1>Example#index</h1>
<p>Find me in app/views/example/index.html.erb</p>

Next, we need to create a small React application that we can insert into this page. Webpacker applications are inserted as packs: small JavaScript bundles, within Rails. We’ll create a new pack in app/javascript/packs/counter.js containing a simple counter component:

import React, {useState} from 'react';
import ReactDOM from 'react-dom';

const Counter = props => {
const [count, setCount] = useState(0);
return <div className='Counter'>
You have clicked the button {count} times.
<button onClick={() => setCount(c => c + 1)}>Click!</button>
</div>;
};

document.addEventListener('DOMContentLoaded', () => {
ReactDOM.render(
<Counter />,
document.body.appendChild(document.createElement('div')),
)
});

This application updates a counter every time the button is clicked.

We can now insert the pack into the web page by adding a single line of code to the template page:

<h1>Example#index</h1>
<p>Find me in app/views/example/index.html.erb</p>
<%= javascript_pack_tag 'counter' %>

Finally, we can run the rails server on port 3000:

rails server

Which will show the page you can see in figure 1-7.

Figure 1-7. A React app embedded in Rails

Discussion

Behind the scenes, as you have probably guessed, Webpacker transforms the application using a copy of webpack, which can be configured with the app/config/webpacker.yml config file.

Webpacker is intended to be used alongside Rails code, rather than as a replacement of it. It’s useful if your Rails application requires a small amount of additional interactivity.

To find out more about Webpacker on the Webpacker Github site.

You can download the source for this recipe from the Github site.

1.8 Create Custom Elements with Preact

Problem

There are sometimes circumstances where it is challenging to add React code into existing content. For example, in some CMS configurations, users are not allowed to insert additional JavaScript into the body of a page. In these cases, it would be useful to have some standardized way to insert JavaScript applications safely into a page.

Solution

Custom elements are a standard way of creating new HTML elements that can be used in a web page. In effect, they are a way of extending the HTML language by making more tags available to a user.

This recipe looks at how a lightweight framework like Preact can be used to create custom elements, which themselves can be served from a third-party service.

Let’s begin by creating a new Preact application. This application will serve the custom element that we will be able to use elsewhere:[7]

preact create default my-element

Now we will change into the app’s directory and add the preact-custom-element library to the project:

cd my-element
npm install preact-custom-element --save

The preact-custom-element library will allow us to register a new custom HTML element in a browser.

Next, we need to modify the app/index.js of the Preact project so that it registers a new custom element, which we will call components/Converter/index.js

import register from 'preact-custom-element';
import Converter from './components/Converter';

register(Converter, 'x-converter', ['currency']);

The register method tells the browser that we want to create a new custom HTML element called <x-converter/>, which has a single property called currency and which will be built using a component defined in ./components/Converter/index.js, which we will define like this:

import {h} from 'preact';
import {useEffect, useState} from "preact/hooks";
import 'style/index.css';

const rates = {gbp: 0.81, eur: 0.92, jpy: 106.64};

export default ({currency = 'gbp'}) => {
const [curr, setCurr] = useState(currency);
const [amount, setAmount] = useState(0);

useEffect(() => {
setCurr(currency);
}, [currency]);

return <div className='Converter'>
<p>
<label htmlFor='currency'>Currency: </label>
<select
name='currency'
value={curr}
onChange={evt => setCurr(evt.target.value)}
>
{
Object.keys(rates).map(r => <option value={r}>{r}</option>)
}
</select>
</p>
<p className='Converter-amount'>
<label htmlFor='amount'>Amount: </label>
<input
name='amount'
size={8}
type="number"
value={amount}
onInput={evt => setAmount(parseFloat(evt.target.value))}
/>
</p>
<p>
Cost:
{((amount || 0) / rates[curr]).toLocaleString('en-US', {
style: 'currency',
currency: 'USD'
})}
</p>
</div>
};

Note

To be compliant with the custom elements specification[8] we must choose a name for our element that begins with a lowercase letter, does not include any uppercase letters, and contains a hyphen. This ensures the name does not clash with any standard element name.

Our Converter component is a very simple currency converter, which in our example, is using a fixed set of exchange rates. If we now start our preact server:

npm run dev

The JavaScript for the custom element will be available at http://localhost:8080/bundle.js

In order to use this new custom element, let’s create a static web page somewhere with this HTML:

<html>
<head>
<script src="https://unpkg.com/babel-polyfill/dist/polyfill.min.js"></script>
<script src="https://unpkg.com/@webcomponents/webcomponentsjs">
</script>
<!-- Replace this with the address of your custom element -->
<script type="text/javascript" src="http://localhost:8080/bundle.js"></script>
</head>
<body>
<h1>Custom Web Element</h1>
<div style="float: right; clear: both">
<!-- This tag will insert the Preact app -->
<x-converter currency="jpy"/>
</div>
<p>This page contains an example custom element called
<code>&lt;x-converter/&gt;</code>,
which is being served from a different location</p>
</body>
</html>

This web page is including the definition of the custom element in the final <script/> of the <head/> element. In order to make sure that the custom element is available across both new and old browsers, we also include a couple of shims from unpkg.com.

Now that the custom element code is included in the web page, we can insert <x-converter/> tags into the code, as if they are part of standard HTML. In our example, we are also passing a currency property, which will be passed through to the underlying Preact component.

Warning

Custom element properties are passed to the underlying component with lowercase names, regardless of how they are defined in the HTML.

We can run this page through a web server, separate from the Preact server. The new custom element is shown in figure 1-8.

Figure 1-8. The custom element embedded in a static page

Discussion

The custom element does not need to be served from the same server as the web page that uses it. This means that custom elements are a way of making embeddable widgets available for online services. Because they can be accessed from elsewhere, you might want to check the Referer header on any incoming request to the component, to prevent any unauthorized usage.

Our example is serving the custom element from Preact’s development server. For a production release, you would probably want to create a static build of the component, which can then be placed on any web server, and will likely be significantly smaller.[9]

Think about security. You may want to tie down which domains can access the element by checking the forward header

You can download the source for this recipe from the Github site.

1.9 Use Storybook for Component Development

Problem

React components are the stable building material of React applications. If they are written carefully, they can be re-used cleanly within and between React applications. But when you are building components, it is sometimes challenging to create the entire set of circumstances that the component will have to deal with. For example, in an asynchronous application, components might frequently be rendered with undefined properties. Will the component still render correctly? Will they show errors when they’re misused?

But if you are building components as part of a complex application, it can be tough to create all of the situations with which your component will need to cope.

Also, if you have specialized UX developers working on your team, it can waste a lot of time if they have to navigate through an application to view the single component they have in development.

It would be useful if there was some way of displaying a component in isolation and passing it example sets of properties.

Solution

Storybook is a tool for displaying libraries of components in various states. It could be described as a gallery for components, but that’s probably selling it short. In reality, Storybook is a tool for component development.

How do we add Storybook to a project? Let’s begin by creating a React application with create-react-app:

npx create-react-app my-app
cd my-app

Now we can add Storybook to the project:

npx -p @storybook/cli sb init

And then start the Storybook server:

npm run storybook

Storybook runs its own server: in this case, we are running it on port 9000, as you can see in figure 1-9. When you are using Storybook, there is no need to run the actual React application.

Figure 1-9. The welcome page in Storybook

Storybook calls a single component rendered with example properties a story. The default installation of Storybook generates sample stories in the src/stories/ directory of the application. This is src/stories/1-Button.stories.js:

import React from 'react';
import { action } from '@storybook/addon-actions';
import { Button } from '@storybook/react/demo';

export default {
title: 'Button',
component: Button,
};

export const Text = () => <Button onClick={action('clicked')}>Hello Button</Button>;

export const Emoji = () => (
<Button onClick={action('clicked')}>
<span role="img" aria-label="so cool">
😀 😎 👍 💯
</span>
</Button>
);

Storybook watches for files named *.stories.js in your source folder, and it doesn’t care where they are, so you are free to create them where you like. One typical pattern places the stories in a folder alongside the component they are showcasing. So if you copy the folder to a different application, you can take stories with it as a form of living documentation.

Figure 1-10 shows what 1-Button.stories.js looks like inside Storybook.

Figure 1-10. An example story

Discussion

Despite its simple appearance, Storybook is actually a very productive development tool. It allows you to focus on one component at a time. Like a kind of visual unit test, it enables you to try out a component in a series of possible scenarios to check that it behaves appropriately.

Storybook also has a large selection of additional add-ons.

The add-ons allow you to:

- Add interactive controls for setting properties (Knobs)

- Include inline documentation for each story (Docs)

- Record snapshots of the HTML to test the impact of changes (Storyshots)

And many, many more.

For further information about Storybook see their web site.

You can download the source for this recipe from the Github site.

1.10 Test Your Code in a Browser with Cypress

Problem

Most React projects include a testing library. The most common is probably @testing-library/react, which comes bundled with create-react-app, or enzyme which is used by preact.

But nothing quite beats testing code inside a real browser, with all of the additional complications that that entails. Traditionally browser testing can be unstable and prone to frequent upgrade problems as browser drivers (such as chromedriver) have to be upgraded every time a browser is.

Add to that the issue of generating test data on a backend server and browser-based testing can be complex to set up and manage.

Solution

The Cypress testing framework avoids many of the downsides of traditional browser testing. It runs in a browser but avoids the need for an external webdriver tool. Instead, it communicates directly with a browser, like Chrome or Electron, over a network port and then injects JavaScript to run much of the test code.

Let’s create an application create-react-app to see how it works:

npx create-react-app my-app

Now let’s go into the app directory and install Cypress:

cd my-app
npm install cypress --save-dev

Before we run Cypress, we need to configure it so that it knows how to find our application. We can do this by editing the cypress.json file in the application directory, and tell if the URL of our app:

{
"baseUrl": "http://localhost:3000/"
}

Once we have started the main application:

yarn start

We can then open Cypress:

npx cypress open

The first time you run Cypress it will install all of the dependencies it needs. We’ll now create a test in the cypress/integration/ directory called screenshot.js. This will be a very simple test which opens the home page and takes a screenshot:

describe('screenshot', () => {
it('should be able to take a screenshot', () => {
cy.visit('/');
cy.screenshot('frontpage');
});
});

You’ll notice that tests are written in Jest format. Once you save the test, it will appear in the main Cypress window, shown in figure 1-11.

Figure 1-11. The Cypress window

If you double-click on the test, it will run it in a browser. The front page of the application will open, and a screenshot will be saved as cypress/screenshots/screenshot.js/frontpage.png.

Discussion

Here are some example commands you can perform with Cypress:

Command

Description

cy.contains('Fred')

Find the element containing Fred

cy.get('.Norman').click()

Click the element with class Norman

cy.get('input').type('Hi!')

Type "Hi!" into the input field

cy.get('h1').scrollIntoView()

Scroll the <h1/> into view

These are just the commands that interact with the web page. But Cypress has another trick up its sleeve. Cypress can also modify the code inside the browser to change the time (cy.clock()), the cookies (cy.setCookie()), the local-storage (cy.clearLocalStorage) and–most impressively–it can even fake requests and responses to an API server.

It does this by modifying the networking functions that are built into the browser so that this code:

cy.route("/api/server?*", [{some: 'Data'}])

Will cause any networking code in the application that makes a call to a server endpoint beginning /api/server?... will return the JSON array [{some: 'Data'}].

This can completely change the way times can develop applications because it decouples the front-end development from the back end. The browser tests can specify what they want data they need without having to create a real server and database.

To learn more about Cypress, visit the documentation site.

You can download the source for this recipe from the Github site.

[1] And yes, this means that Gatsby has TypeScript support built-in.

[2] You can do this in most operating systems by pressing CTRL-C.

[3] The name is intentionally similar to create-react-app. The maintainer of Razzle, Jared Palmer, lists create-react-app as one of the inspirations for Razzle.

[4] See recipe 2 in this chapter

[5] See the Gatsby and Razzle recipes elsewhere in this chapter.

[6] By pressing CTRL=-C

[7] For more information on creating Preact applications, see the Preact recipe earlier in this chapter.

[8] See the WHATWG specification for further details on custom elements and naming conventions.

[9] For further details on shrinking Preact downloads, see the Preact recipe earlier in this chapter.

Chapter 2. Routing

A note for Early Release readers

With Early Release ebooks, you get books in their earliest form — the authors’ raw and unedited content as they write — so you can take advantage of these technologies long before the official release of these titles.

This will be the 2nd chapter of the final book. Please note that the GitHub repo will be made active later on.

If you have comments about how we might improve the content and/or examples in this book, or if you notice missing material within this chapter, please reach out to the editor at ccollins@oreilly.com.

In this chapter, we are going to look at recipes using React routes and the react-router-dom library.

react-router-dom uses declarative routing: that means you treat routes as you would any other React component. React routes are obviously different from buttons, text fields, and blocks of text because they have no visual appearance. But in most other ways, they are very similar to those buttons and blocks of text. Routes live in the virtual DOM tree of components. They listen for changes in the current browser location and allow you to switch on and switch off parts of the interface. They are what give Single Page Applications the appearance of multi-page applications.

Used well, they can make your application feel like any other web site. Users will be able to bookmark sections of your application, as they might bookmark a page from Wikipedia. They can go backward and forwards in their browser history, and your interface will behave properly. If you are new to React, then it is well worth your time looking deeply into the power of routing.

2.1 Create Desktop/Mobile Interfaces with Responsive Routes

Problem

Most apps will be consumed by people on both mobile and laptop computers, which means you probably want your React application to work well across all screen sizes. This might involve relatively simple CSS changes to adjust the sizing of text and screen layout, as well as more substantial changes, which can give mobile and desktop users very different experiences when navigating around your site.

Our example application shows the names and addresses of a list of people. On a desktop, it looks like this:

Figure 2-1. The desktop view of the app

But this won’t work very well on a mobile device, which might only have space to display either the list of people, or the details of one person, but not both.

What can we do in React to provide a custom navigation experience for both mobile and desktop users, without having to create two completely separate versions of the application?

Solution

We’re going to use responsive routes. A responsive route changes according to the size of the user’s display. Our existing application uses a single route for displaying the information for a person: /people/:id

When you navigate to this route, the browser shows a page in Figure 2-1. All people are listed down the left-hand side, with the person matching :id highlighted. And the selected person’s details are displayed on the right.

We’re going to modify our application so that it will also cope with an additional route at /people. Then we will make the routes responsive so that the user will see different things on different devices:

Route

Mobile

Desktop

/people

Show list of people

Redirect to /people/<some-id>

/people/<id>

Show details for <id>

Show list of people and details of <id>

What ingredients will we need to do this? First, we need to install react-router-dom if our application does not already have it:

npm install react-router-dom --save

The react-router-dom library allows us to coordinate the current location of the browser with the state of our application. Next, we will install the react-media library, which allows us to create React components that respond to changes in the size of the display screen.

npm install react-media --save

Now we’re going to create a responsive PeopleContainer component that will manage the routes that we want to create. On small screens, our component will display either a list of people or the details of a single person. On large screens, it will show a combined view of a list of people on the left and the details of a single person on the right.

The PeopleContainer will use the Media component from react-media. The Media component performs a similar job to the CSS @media rule: it allows you to generate output for a specified range of screen sizes. The Media component accepts a queries property which allows you to specify a set of screen sizes. We’re going to define a single screen size–small–that we’ll use as the break between mobile and desktop screens:

<Media queries={{
small: "(max-width: 700px)"
}}>
...
</Media>

The Media component takes a single child component, which it expects to be a function. This function is given a size object which can be used to tell what the current screen size is. In our example, the size object will have a small attribute, which we can use to decide what other components to display:

<Media queries={{
small: "(max-width: 700px)"
}}>
{
size => size.small ? [SMALL SCREEN COMPONENTS] : [BIG SCREEN COMPONENTS]
}
</Media>

Before we look at the details of what code we are going to return for large and small screens, it’s worth taking a look at how we will mount the PeopleContainer in our application. This is going to be our main App component:

import React from 'react';
import {BrowserRouter, Link, Route, Switch} from 'react-router-dom';
import PeopleContainer from "./PeopleContainer";

function App() {
return (
<BrowserRouter>
<Switch>
<Route path='/people'>
<PeopleContainer/>
</Route>
<Link to='/people'>People</Link>
</Switch>
</BrowserRouter>
);
}

export default App;

We are using the BrowserRouter from react-router-dom. This is the link between our code and the HTML5 history API in the browser. We need to wrap all of our routes in a Router so that they have access to the browser’s current address.

Inside the BrowserRouter, we have a Switch. The Switch looks at the components inside it, looking for a Route that matches the current location. Here we have a single Route matching paths that begin with /people. If that’s true, we display the PeopleContainer. If no route matches, we fall through to the end of the Switch and just render a Link to the /people path. So when someone goes to the front page of the application, they only see a link to the People page.

So we know if we’re in the PeopleContainer, we’re already on a route that begins with /people/…. If we’re on a small screen, we need to either show a list of people or display the details of a single person, but not both. We can do this with Switch:

<Media queries={{
small: "(max-width: 700px)"
}}>
{
size => size.small ? [SMALL SCREEN COMPONENTS]
<Switch>
<Route path='/people/:id'>
<Person/>
</Route>
<PeopleList/>
</Switch>
: [BIG SCREEN COMPONENTS]
}
</Media>

On a small device, the Media component will call its child function with a value that means size.small is true. Our code will render a Switch that will show a Person component if the current path contains an id. Otherwise, the Switch will fail to match that Route and will instead render a PeopleList.

Ignoring the fact that we’ve yet to write the code for large screens, if we were to run this code right now on a mobile device, and hit the People link on the front page, we would navigate to /people which could cause the application to render the PeopleList component. The PeopleList component[1] displays a set of links to people with paths of the form /people/id. When someone selects a person from the list, our components are re-rendered, and this time PeopleContainer displays the details of a single person.

Figure 2-2. In mobile view: the list of people (left) which links to a person’s details (right)

So far, so good. Now we need to make sure that our application still works for larger screens. We need to generate responsive routes in PeopleContainer for when size.small is false. If the current route is of the form /people/id we can display the PeopleList component on the left, and the Person component on the right:

<div style={{display: 'flex'}}>
<PeopleList/>
<Person/>
</div>

Unfortunately that doesn’t handle the case where the current path is /people. For that, we need another Switch which will either display the details for a single person, or will redirect to /people/first-person-id for the first person in the list of people.

<div style={{display: 'flex'}}>
<PeopleList/>
<Switch>
<Route path='/people/:id'>
<Person/>
</Route>
<Redirect to={`/people/${people[0].id}`}/>
</Switch>
</div>

The Redirect component doesn’t perform an actual browser redirect. It simply updates the current path to /people/first-person-id, which causes the PeopleContainer to re-render. It’s similar to making a call to history.push() in JavaScript, except it doesn’t add an extra page to the browser history. If a person navigates to /people, the browser will simply change it’s location to /people/first-person-id.

If we were now to go to /people on a laptop or larger tablet, we would see the list of people next to the details for one person.

Figure 2-3. What you see at http://localhost:3000/people on a large display

Here is the final version of our PeopleContainer

import React from 'react';
import Media from "react-media";
import {Redirect, Route, Switch} from "react-router-dom";
import Person from "./Person";
import PeopleList from "./PeopleList";
import people from './people';

export default () => {
return <Media queries={{
small: "(max-width: 700px)"
}}>
{
size => size.small ?
<Switch>
<Route path='/people/:id'>
<Person/>
</Route>
<PeopleList/>
</Switch>
:
<div style={{display: 'flex'}}>
<PeopleList/>
<Switch>
<Route path='/people/:id'>
<Person/>
</Route>
<Redirect to={`/people/${people[0].id}`}/>
</Switch>
</div>
}
</Media>;
};

Discussion

Declarative routing inside components can seem an odd thing when you first meet it. If you’ve used a centralized routing model before, declarative routes may at first seem messy. They do, after all, spread the wiring of your application across who-knows-how-many components, rather than keeping it all neatly in a single file. Rather than creating clean components that know nothing of the outside world, you are suddenly giving the intimate knowledge of the paths used in the application, which might make them less portable.

However, responsive routes show the real power of declarative routing. If you’re concerned about your components knowing too much about the paths in your application, consider extracting the path-strings into a shared file. That way, you will have the best of both worlds: components that modify their behavior based upon the current path, and a centralized set of path configurations.

You can download the source for this recipe from the Github site.

2.2 Move State into Routes to Create Deep Links

Problem

It is often useful to manage the internal state of a component with the route that the component is displayed. For example, this is a React component that displays two tabs of information: one for people and one for /offices.

import React, {useState} from "react";
import People from "./People";
import Offices from "./Offices";

import "./About.css";

export default () => {
const [tabId, setTabId] = useState("people")

return <div className='About'>
<div className='About-tabs'>
<div onClick={() => setTabId("people")}
className={tabId === "people" ? "About-tab active" : "About-tab"}
>
People
</div>
<div onClick={() => setTabId("offices")}
className={tabId === "offices" ? "About-tab active" : "About-tab"}
>
Offices
</div>
</div>
{tabId === "people" && <People/>}
{tabId === "offices" && <Offices/>}
</div>;
}

When a user clicks on a tab, an internal tabId variable is updated, and the People of Offices component is displayed. This is what it looks like:

Figure 2-4. By default, the OldAbout component shows people details

What’s the problem? The component works, but if we select the Offices tab and then refresh the page, the component resets to the People tab. Likewise, we can’t bookmark the page when it’s on the Offices tab. We can create a link anywhere else in the application which takes us directly to the Offices. Accessibility hardware is less likely to notice that the tabs are working as hyperlinks because they are not rendered in that way.

Solution

We are going to the tabId state from the component into the current browser location. So instead of rendering the component at /about and then using onClick events to change internal state, we are instead going to have routes to /about/people and /about/offices which display one tab or the other. The tab selection will survive a browser refresh. We can bookmark the page on a given tab. We can jump to a given tab. And we make the tabs themselves real hyperlinks, which will be recognized as such by anyone navigating with a keyboard or screen reader.

What ingredients do we need? Just one: react-router-dom:

npm install react-router-dom --save

react-router-dom will allow us to synchronize the current browser URL with the components that we render on screen.

Our existing is already using react-router-dom to display the OldAbout component at path /oldabout as you can see from this fragment of code from the App.js file:

<Switch>
<Route path="/oldabout">
<OldAbout/>
</Route>
<p>Choose an option</p>
</Switch>

You can see the full code for this file at the Github repository.

We’re going to create a new version of the OldAbout component, called About and we’re going to mount it at its own route:

<Switch>
<Route path="/oldabout">
<OldAbout/>
</Route>
<Route path="/about/:tabId?">
<About/>
</Route>
<p>Choose an option</p>
</Switch>

This means that we will be able to open both versions of the code in the example application.

Our new version is going to appear to be virtually identical to the old component. We’ll extract the tabId from the component, and move it into the current path.

Setting the path of the Route to /about/:tabId? means that both /about, /about/offices, /about/people will all mount our component. The "?" indicates that the tabId parameter is optional.

We’ve now done the first part: we’ve put the state of the component into the path that displays it. Now we need to update the component to interact with the route, rather than an internal state variable.

In the OldAbout component, we had onClick listeners on each of the tabs:

<div onClick={() => setTabId("people")}
className={tabId === "people" ? "About-tab active" : "About-tab"}
>
People
</div>
<div onClick={() => setTabId("offices")}
className={tabId === "offices" ? "About-tab active" : "About-tab"}
>
Offices
</div>

We’re going to convert these into Link components, going to /about/people and /about/offices. In fact, we’re going to convert them into NavLink components. A NavLink is like a link, except it has the ability to set an additional class-name, if the place it’s linking to is the current location. These means we don’t need the className logic in the original code:

<NavLink to="/about/people"
className="About-tab"
activeClassName="active">
People
</NavLink>
<NavLink to="/about/offices"
className="About-tab"
activeClassName="active">
Offices
</NavLink>

We no longer set the value of a tabId variable. We instead go to a new location with a new tabId value in the path.

But what do we do to read the tabId value? The OldAbout code displays the current tab contents like this:

{tabId === "people" && <People/>}
{tabId === "offices" && <Offices/>}

This code can be replaced with a Switch and a couple of Route components:

<Switch>
<Route path='/about/people'>
<People/>
</Route>
<Route path='/about/offices'>
<Offices/>
</Route>
</Switch>

We’re now almost finished. There’s just one step remaining: what to do if the path is simply /about and contains no tabId.

The OldAbout sets a default value for tabId when it first creates the state:

const [tabId, setTabId] = useState("people")

We can achieve the same effect by adding a Redirect to the end of our Switch. If no Route matches the current path, we change the address to /about/people. This will cause a re-render of the About component and the People tab will be selected by default:

<Switch>
<Route path='/about/people'>
<People/>
</Route>
<Route path='/about/offices'>
<Offices/>
</Route>
<Redirect to='/about/people'/>
</Switch>

This is our completed About component:

import React from "react";
import {NavLink, Redirect, Route, Switch} from "react-router-dom";
import "./About.css";
import People from "./People";
import Offices from "./Offices";

export default () =>
<div className='About'>
<div className='About-tabs'>
<NavLink to="/about/people"
className="About-tab"
activeClassName="active">
People
</NavLink>
<NavLink to="/about/offices"
className="About-tab"
activeClassName="active">
Offices
</NavLink>
</div>
<Switch>
<Route path='/about/people'>
<People/>
</Route>
<Route path='/about/offices'>
<Offices/>
</Route>
<Redirect to='/about/people'/>
</Switch>
</div>;

We no longer need an internal tabId variable, and we now have a purely declarative component.

Figure 2-5. Going to http://localhost/about/offices with the new component

Discussion

Moving state out your components and into the address bar can simplify your code, but this is merely a fortunate side-effect. The real value is that your application starts to behave less like an application, and more like a web site. Pages can be bookmarked, and the browser’s Back and Forward buttons work correctly. Managing more state in routes is not an abstract design decision, it’s a way of making your application less surprising to users.

You can download the source for this recipe from the Github site.

2.3 Use MemoryRouter for Unit Testing

Problem

We use routes in React applications so that we make more of the facilities of the browser. We can bookmark pages, we can create deep links into an app, and we can go backward and forwards in history.

However, once we use routes, we are making the component dependent upon something outside itself: the browser location. That might not seem like too big an issue, but it does have consequences.

Let’s say we want to unit test a route-aware component. As an example, let’s create a unit test for the About component we build in recipe 2 of this chapter. This is what a unit test might look like.[2]

describe('About component', () => {
it('should show people', () => {
const { getByText } = render (<About/>);
expect(getByText('Kip Russel')).toBeInTheDocument();
});
});

This unit test renders the component and then checks that it can find the name “Kip Russel” appearing in the output. When we run this test, we get the following error:

console.error node_modules/jsdom/lib/jsdom/virtual-console.js:29
Error: Uncaught [Error: Invariant failed: You should not use <NavLink> outside a <Router>]

This happened because a NavLink could not find a Router higher in the component tree. That means we need to wrap the component in a Router before we test it.

Also, we might want to write a unit test that checks that the About performs behaves correctly when it’s mounted on a specific route. Even if we provide some sort of Router component, how will we be able to fake a particular route?

It’s not just an issue with unit tests. If we’re using a library tool like Storybook[3], we might want to show an example of how a component appears when it is mounted on a particular.

What we need is something like a real browser router, but one which allows us to specify its behavior.

Solution

The react-router-dom library provides just such a router: MemoryRouter. The MemoryRouter appears to the outside world, just like BrowserRouter. The difference is that while the BrowserRouter is an interface to the underlying browser history API, the MemoryRouter has no dependency upon the browser at all. It can keep track of the current location, it can go backward and forwards in history, but it does it all through simple memory structures.

Let’s take another look at that failing unit test. Instead of just rendering the About component, let’s wrap it in a MemoryRouter:

describe('About component', () => {
it('should show people', () => {
const { getByText } = render (<MemoryRouter><About/></MemoryRouter>);

expect(getByText('Kip Russel')).toBeInTheDocument();
});
});

Now, when we run the test, it works. That’s because the MemoryRouter injects a mocked-up version of the API into the context. That makes it available to all of its child components. When the About component wants to render a Link or a Route, it’s now able to because history is available in the context.

But the MemoryRouter has an additional advantage. Because it’s faking the browser history API, it can be given a completely fake history, using the initialEntries property. The initialEntries property should be set to an array of history entries. If you pass a single value array, it will be interpreted as the current location. That allows you to write unit tests that check for component behavior when it’s mounted on a given route:

describe('About component', () => {
it('should show offices if in route', () => {
const {getByText} = render(<MemoryRouter initialEntries={[
{pathname: '/about/offices'},
]}>
<About/>
</MemoryRouter>);

expect(getByText('South Dakota')).toBeInTheDocument();
});
});

We can actually use a real BrowserRouter inside Storybook because we’re in a real browser. The MemoryRouter still has the advantage of being able to fake a current location, as we do in the ToAboutOffices story:

Figure 2-6. Using MemoryRouter we can fake the /about/offices route

Discussion

Routers let you separate the details of where you want to go, from how you’re going to get there. In this recipe, we see one advantage of this separation: we can create a fake browser location to examine component behavior on different routes. It is this separation that allows you to change the way that links are followed without breaking your application. If you convert your Single Page Application to a Server-Side Rendered application, you swap your BrowserRouter for a StaticRouter. The links that used to make calls into the browser’s history API will now become native hyperlinks, that cause the browser to make native page loads. Routers are an excellent example of the advantages of splitting policy (what you want to do) from mechanisms (how you’re going to do it).

You can download the source for this recipe from the Github site.

2.4 Use Prompt for Page Exit Confirmations

Problem

Sometimes you need to ask a user to confirm that they want to leave a page if they’re in the middle editing something. This seemingly simple task can be complicated because it relies on spotting when the user presses the back button and then finding a way to intercept the move back through history and potentially canceling it.

Figure 2-7. What if you want someone to confirm that they wish to leave a page?

What if there are several pages in the application which need the same feature? Is there a simple way to create this feature across any component that needs it?

Solution

The react-router-dom library includes a component called Prompt, which is explicitly designed to get users to confirm that they wish to leave a page.

The only ingredient we really need for this recipe is the react-router-dom library itself:

npm install react-router-dom --save

Let’s say we are going to have a component called Important mounted at /important, which allows a user to edit a piece of text.

import React, {useEffect, useState} from "react";

export default () => {
let initialValue = "Initial value";

const [data, setData] = useState(initialValue);
const [dirty, setDirty] = useState(false);

useEffect(() => {
if (data !== initialValue) {
setDirty(true);
}
}, [data, initialValue]);

return <div className='Important'>
<textarea onChange={evt => setData(evt.target.value)}
cols={40} rows={12}>
{data}
</textarea>
<br/>
<button onClick={() => setDirty(false)}
disabled={!dirty}>Save</button>
</div>;
}

Important is already tracking whether the text in the textarea has changed from the original value. If the text is different, the value is dirty is true. How do we ask the user to confirm they want to leave the page if they press the Back button when dirty is true?

We add in a Prompt component:

return <div className='Important'>
<textarea onChange={evt => setData(evt.target.value)}
cols={40} rows={12}>
{data}
</textarea>
<br/>
<button onClick={() => setDirty(false)}
disabled={!dirty}>Save</button>
<Prompt
when={dirty}
message={() => "Do you really want to leave?"}
/>
</div>;

If the user edits the text and then hits the Back button, the Prompt appears:

Figure 2-8. The Prompt asks the user to confirm they want to leave

Adding the confirmation is very simple. But the default prompt interface is a simple JavaScript dialog. It would be useful if we could decide for ourselves how we want the user to confirm they’re leaving.

To demonstrate how we can do this, let’s add in the Material-UI component library to the application:

npm install '@material-ui/core' --save

The Material-UI library is a React implementation of Google’s Material Design standard. We’ll use it as an example of how to replace the standard Prompt interface with something more customized.

The Prompt component does not actually any UI itself. Instead, when Prompt is rendered, it asks the current Router object to show the confirmation. By default, BrowserRouter shows the default JavaScript dialog, but you can replace this with your own code.

When the BrowserRouter is added to the component tree, we can pass it a property called getUserConfirmation:

<div className="App">
<BrowserRouter
getUserConfirmation={(message, callback) => {
// Custom code goes here
}}
>
<Switch>
<Route path='/important'>
<Important/>
</Route>
</Switch>
</BrowserRouter>
</div>

The getUserConfirmation property should be set to a function that accepts two parameters: the message that should be presented to the user, and a callback function.

When the user presses the Back button, the Prompt component will run getUserConfirmation, and then wait for the callback function to be called with the value true or false.

The callback function allows use to return the user’s response asynchronously. The Prompt component will wait while we ask the user what want to do. That allows use to create out own custom interface.

Let’s create a custom Material-UI dialog called Alert. We’ll show this instead of the default JavaScript modal:

import React from "react";
import Button from '@material-ui/core/Button';
import Dialog from '@material-ui/core/Dialog';
import DialogActions from '@material-ui/core/DialogActions';
import DialogContent from '@material-ui/core/DialogContent';
import DialogContentText from '@material-ui/core/DialogContentText';
import DialogTitle from '@material-ui/core/DialogTitle';

export default ({open, title, message, onOK, onCancel}) => {
return <Dialog
open={open}
onClose={onCancel}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle id="alert-dialog-title">{title}</DialogTitle>
<DialogContent>
<DialogContentText id="alert-dialog-description">
{message}
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={onCancel} color="primary">
Cancel
</Button>
<Button onClick={onOK} color="primary" autoFocus>
OK
</Button>
</DialogActions>
</Dialog>;
}

Of course, there is no reason why we need to display a dialog. We could show a countdown timer or a snackbar message. We could even automatically save the user’s changes for them. But we use the custom Alert dialog in this case.

How will use the Alert component in our interface? The first thing we’ll need to do is create our own getUserConfirmation function. We’ll store the message and the callback function, and then set a boolean value saying that we want to open the Alert dialog:

const [confirmOpen, setConfirmOpen] = useState(false);
const [confirmMessage, setConfirmMessage] = useState();
const [confirmCallback, setConfirmCallback] = useState();

return (
<div className="App">
<BrowserRouter
getUserConfirmation={(message, callback) => {
setConfirmMessage(message);
// Use this setter form because callback is a function
setConfirmCallback(() => callback);
setConfirmOpen(true);
}}
>
.....

It’s worth noting that when we store the callback function, we use setConfirmCallback(() ⇒ callback) instead of simply writing setConfirmCallback(callback). That’s because the setters returned by the useState hook will execute any function passed to them, rather than store them.

We can then use the values of confirmMessage, confirmCallback, and confirmOpen to render the Alert in the interface.

This is the complete App.js file:

import React, {useState} from 'react';
import './App.css';
import {BrowserRouter, Link, Route, Switch} from "react-router-dom";
import Important from "./Important";
import Alert from './Alert';

function App() {
const [confirmOpen, setConfirmOpen] = useState(false);
const [confirmMessage, setConfirmMessage] = useState();
const [confirmCallback, setConfirmCallback] = useState();

return (
<div className="App">
<BrowserRouter
getUserConfirmation={(message, callback) => {
setConfirmMessage(message);
// Use this setter form because callback is a function
setConfirmCallback(() => callback);
setConfirmOpen(true);
}}
>
<Alert open={confirmOpen}
title='Leave page?'
message={confirmMessage}
onOK={() => {
confirmCallback(true);
setConfirmOpen(false);
}}
onCancel={() => {
confirmCallback(false);
setConfirmOpen(false);
}}
/>
<Switch>
<Route path='/important'>
<Important/>
</Route>
<div>
<h1>Home page</h1>
<Link to='/important'>Go to important page</Link>
</div>
</Switch>
</BrowserRouter>
</div>
);
}

export default App;

Now when a user backs out of an edit, they see the custom dialog.

Figure 2-9. The custom Alert appears when the user presses the back button

Discussion

In this recipe, we have re-implemented the Prompt modal using a component library, but you don’t need to be limited to just replacing one dialog box with another. There is no reason why, if someone leaves a page, that you couldn’t do something else: such as store the work-in-progress somewhere so that they could return to it later. The asynchronous nature of the getUserConfirmation function allows this flexibility. It’s another example of how react-router-dom abstracts away a cross-cutting concern.

You can download the source for this recipe from the Github site.

2.5 Add Page Transitions With react-transition-group

Problem

Native and desktop applications often use animation to visually connect different elements together. If you press an item in a list, it expands to show you the details. Swiping left or right can be used to indicate whether a user accepts or rejects an option.

Animations, therefore, are often used to indicate a change in location. They zoom in on the details. They take you to the next person on the list.

Changing locations in a React application. But how can we animate when we move from one location to another?

Solution

For this recipe, we’re going to need the react-router-dom library and the react-transition-group library.

npm install react-router-dom --save
npm install react-transition-group --save

We’re going to animate the About component that we’ve used previously[4]. The About component has two tabs called People and Offices, which are displayed for routes /about/people and /about/offices.

When someone clicks on one of the tabs, we’re going to fade-out the content of the old tab and then fade in the content of the new tab. Although we’re using a fade, there’s no reason why we couldn’t use a more complex animation, such as sliding the tab contents left or right[5]. However, a simple fade animation will more clearly demonstrate how it works.

Inside the About component, the tab contents are rendered by People and Offices components within distinct routes:

import React from "react";
import {NavLink, Redirect, Route, Switch} from "react-router-dom";
import "./About.css";
import People from "./People";
import Offices from "./Offices";

export default () =>
<div className='About'>
<div className='About-tabs'>
<NavLink to="/about/people"
className="About-tab"
activeClassName="active">
People
</NavLink>
<NavLink to="/about/offices"
className="About-tab"
activeClassName="active">
Offices
</NavLink>
</div>
<Switch>
<Route path='/about/people'>
<People/>
</Route>
<Route path='/about/offices'>
<Offices/>
</Route>
<Redirect to='/about/people'/>
</Switch>
</div>;

We need to animate the components inside the Switch component. We’ll need two things to do this:

- Something to track when the location has changed

- Something to animate the tab contents when that happens

How do we know when the location has changed? We can get the current location from the useLocation hook from react-router-dom:

const location = useLocation();

Now onto the more complex task: the animation itself. What follows is actually quite a complex sequence of events, but it is worth taking the time to understand it.

When we are animating from one component to another, we need to keep both components on the page. As the Offices component fades out, the People component fades in.[6] This is done by keeping both components in a transition group. We can create a transition group by wrapping our animation in a TransitionGroup component. We also need a CSSTransition component to coordinate the details of the CSS animation.

Our updated code wraps the Switch in both a TransitionGroup and a CSSTransition:

import React from "react";
import {NavLink, Redirect, Route, Switch, useLocation} from "react-router-dom";
import People from "./People";
import Offices from "./Offices";
import {CSSTransition, TransitionGroup} from "react-transition-group";

import "./About.css";
import "./fade.css";

export default () => {
const location = useLocation();

return <div className='About'>
<div className='About-tabs'>
<NavLink to="/about/people"
className="About-tab"
activeClassName="active">
People
</NavLink>
<NavLink to="/about/offices"
className="About-tab"
activeClassName="active">
Offices
</NavLink>
</div>
<TransitionGroup className='About-tabContent'>
<CSSTransition
key={location.key}
classNames="fade"
timeout={500}
>
<Switch location={location}>
<Route path='/about/people'>
<People/>
</Route>
<Route path='/about/offices'>
<Offices/>
</Route>
<Redirect to='/about/people'/>
</Switch>
</CSSTransition>
</TransitionGroup>
</div>;
}

Notice that we pass the location.key to the key of the CSSTransition group, and we pass the location to the Switch component. When the user clicks on one of the tabs, the location changes, which refreshes the About component. The TransitionGroup will keep the existing CSSTransition in the tree of components until its timeout occurs: in 500 milliseconds. But it will now also have a second CSSTransition component.

Each of these CSSTransition components will keep their child components alive.

Figure 2-10. The TransitionGroup keeps both the old and new components in the virtual DOM.

This is why we pass the location value to the Switch components: we need the Switch for the old tab, and the Switch for the new tab to keep rendering their routes.

So now, onto the animation itself. The CSSTransition component accepts a property called classNames[7], which we have set to the value fade. CSSTransition will use to generate four distinct class-names:

- fade-enter

- fade-enter-active

- fade-exit

- fade-exit-active

The fade-enter class is for components that are about to start to animate into view. The fade-enter-active class is applied to components that are actually animating. fade-exit and fade-exit-active are for components that are beginning or animating their disappearance.

The CSSTransition component will add these class-names to their immediate children. If we are animating from the Offices tab to the People tab, then the old CSSTransition will add the fade-enter-active class to the People HTML, and add the fade-exit-active to the Offices HTML.

All that’s left to do is define the CSS animations themselves:

.fade-enter {
opacity: 0;
}
.fade-enter-active {
opacity: 1;
transition: opacity 250ms ease-in;
}
.fade-exit {
opacity: 1;
}
.fade-exit-active {
opacity: 0;
transition: opacity 250ms ease-in;
}

The fade-enter- classes use CSS transitions to change the opacity of the component from 0 to 1. The fade-exit- classes animate the opacity from 1 back to 0. It’s generally a good idea to keep the animation class definitions in their own CSS file. That way, they can be reused for other animations.

The animation is complete. When the user clicks on a tab, they see the contents cross-fade from the old data to the new data.

Figure 2-11. The contents of the tab fade from offices to people

Discussion

Animations can be quite irritating when used poorly. Each animation you add should have some intent. If you find that you want to add an animation just because you think it will be attractive, then you will almost certainly find that the users will dislike it. Generally, it is best to ask a few questions before adding an animation:

- Will this animation clarify the relationship between two routes? Are you zooming-in to see more detail, or moving across to look at a related item?

- How short should the animation be? Any longer than half a second is probably too much.

- What is the impact on performance? CSS transitions usually have minimal effect if the browser hands the work off to the GPU. But what happens in an old browser on a mobile device?

You can download the source for this recipe from the Github site.

2.6 Create Secured Routes

Problem

Most applications need to prevent access to particular routes until a person logs in. But how do you secure some routes and not others? Is it possible to separate the security mechanisms from the user interface elements for logging in and logging out? And how do you do it without writing a vast amount of code?

Solution

Let’s look at one way to implement route-based security in a React application. This application contains a home page (/), a public page with no security (/public), and it also has two private pages (/private1 and /private2) that we need to secure:

import React from 'react';
import './App.css';
import {BrowserRouter, Route, Switch} from "react-router-dom";
import Public from "./Public";
import Private1 from "./Private1";
import Private2 from "./Private2";
import Home from "./Home";

function App() {
return (
<div className="App">
<BrowserRouter>
<Switch>
<Route exact path='/'>
<Home/>
</Route>
<Route path='/private1'>
<Private1/>
</Route>
<Route path='/private2'>
<Private2/>
</Route>
<Route exact path='/public'>
<Public/>
</Route>
</Switch>
</BrowserRouter>
</div>
);
}

export default App;

We’re going to build the security system using a context. A context is a place where data can be stored by a component and made available to the component’s children. A BrowserRouter uses a context to pass routing information to the Route components within it.

We’re going to create a custom context called SecurityContext

import React from "react";

export default React.createContext({});

The default value of our context is an empty object. We need something that will places function into the context for logging in and logging out. We’ll do that by creating a SecurityProvider.

import React, {useState} from "react";
import SecurityContext from "./SecurityContext";

export default (props) => {
const [loggedIn, setLoggedIn] = useState(false);

return <SecurityContext.Provider
value={{
login: (username, password) => {
// Note to engineering team:
// Maybe make this more secure...
if (username === 'fred' && password === 'password') {
setLoggedIn(true);
}
},
logout: () => setLoggedIn(false),
loggedIn
}}>
{props.children}
</SecurityContext.Provider>
};

This is obviously not what you would use in a real system. You would probably create a component that logged in and logged out using some sort of web service or third party security system. But in our example, the SecurityProvider keeps track of whether we are logged in using a simple loggedIn boolean value. The SecurityProvider puts three things into the context:

- A function for logging (login())

- A function for logging out (logout())

- A boolean value saying whether we are logged in or out (loggedIn)

These three things will be available to any components placed inside a SecurityProvider component. To allow any component inside a SecurityProvider to access these functions, we’ll add a custom hook called useSecurity:

import SecurityContext from "./SecurityContext";
import {useContext} from "react";

export default () => useContext(SecurityContext);

Now that we have a SecurityProvider we need a way to use it to secure a sub-set of the routes. We’ll create another component, called SecureRoute:

import React from 'react';
import Login from "./Login";
import {Route} from "react-router-dom";
import useSecurity from "./useSecurity";

export default (props) => {
const {loggedIn} = useSecurity();

return <Route {...props}>
{loggedIn ? props.children : <Login/>}
</Route>;
}

The SecureRoute component gets the current loggedIn status from the SecurityContext (using the useSecurity() hook), and if the user is logged-in, it renders the contents of the route. If the user is not logged in, it displays a log-in form.[8]

The LoginForm calls the login() function, which–if successful–will re-render the SecureRoute and then show the secured data.

How do we use all of these new components? This is an updated version of the App.js file:

import React from 'react';
import './App.css';
import {BrowserRouter, Route, Switch} from "react-router-dom";
import Public from "./Public";
import Private1 from "./Private1";
import Private2 from "./Private2";
import Home from "./Home";
import SecurityProvider from "./SecurityProvider";
import SecureRoute from "./SecureRoute";

function App() {
return (
<div className="App">
<BrowserRouter>
<SecurityProvider>
<Switch>
<Route exact path='/'>
<Home/>
</Route>
<SecureRoute path='/private1'>
<Private1/>
</SecureRoute>
<SecureRoute path='/private2'>
<Private2/>
</SecureRoute>
<Route exact path='/public'>
<Public/>
</Route>
</Switch>
</SecurityProvider>
</BrowserRouter>
</div>
);
}

export default App;

The SecurityProvider wraps our whole routing system, making login(), logout(), and loggedIn available to each SecureRoute.

What does the application look like when we run it?

Figure 2-12. The home page has links to the other pages

If we click on the link for the Public Page, we see it contents, no problem.

Figure 2-13. The public page is available without logging in

But if we click on Private Page 1, we’re presented with the log-in screen:

[[unique_id14] .You need to log in before you can see Private Page 1 image::images/ch02-auth-zone-6-privatePage1Locked.png["You need to log in before you can see Private Page 1"]

If you log in with the username fred, and password password, you will then see the private content

Figure 2-14. The content of Private Page 1 after log-in

Discussion

Real security is only ever provided by secured back-end services. However, secured routes prevent a user from stumbling into a page that will be unable to read data from the server.

A better implementation of the SecurityProvider would defer to some third-party OAuth tool or other security services. But by splitting the SecurityProvider from the security UI (Login and Logout) and the main application, you should be able to modify the security mechanisms over time without having to change a lot of code in your application.

If you want to see how your components behave when people are logged in and out, you can always create a mocked version of the SecurityProvider for use in unit tests.

You can download the source for this recipe from the Github site.

[1] We’re won’t show the code for the PeopleList here, but it is available on Github

[2] We are using the React testing-library in this example.

[3] See recipe 1-9 in chapter 1.

[4] See recipes 2 and 3 in this chapter

[5] This is a common feature of third-party tabbed components. The animation reinforces in the user’s mind that they are moving left and right through the tabs. This is particularly true if we allow the user to change the tab by swiping left or right.

[6] The code uses relative positioning to place both components in the same position during the fade.

[7] Please note, this is plural, to distinguish it from the standard className attribute.

[8] We’ll omit the contents of the Login component here, but the code is available on the Github repository.

1. Preface

1. Conventions Used in This Book

2. Using Code Examples

3. O’Reilly Online Learning

4. How to Contact Us

2. Creating Applications

1. 1.1. Create a Vanilla App with create-react-app

2. 1.2. Build Content-Rich Apps with Gatsby

3. 1.3. Build Universal Apps with Razzle

4. 1.4. Manage Server and Client Code with Next.js

5. 1.5. Create a Tiny App with Preact

6. 1.6. Build Libraries with NWB

7. 1.7. Add React to Rails with Webpacker

8. 1.8. Create Custom Elements with Preact

9. 1.9. Use Storybook for Component Development

10. 1.10. Test Your Code in a Browser with Cypress

3. Routing

1. 2.1. Create Desktop/Mobile Interfaces with Responsive Routes

2. 2.2. Move State into Routes to Create Deep Links

3. 2.3. Use MemoryRouter for Unit Testing

4. 2.4. Use Prompt for Page Exit Confirmations

5. 2.5. Add Page Transitions With react-transition-group

6. 2.6. Create Secured Routes