There's a lot to learn with React, but some basic ideas can make building apps simpler, with code that is cleaner and easier to understand and debug. Those ideas are:

  • Use modern functional JavaScript style everywhere. Avoid assignment-heavy state-mutating code.
  • Build tiny vertical slices of end-to-end user value.
  • Implement static data display first, static state display second, interaction last.
  • Style from the start.

More on this below.

Even if you know React, do this activity. Very few students have yet learned how to use React with Hooks well. If you're not sure, take a look at this first code sample.

Prerequisites

Brush Up on Modern JavaScript

You should be familiar with modern JavaScript coding. Much has changed in the past few years. A good reference is Modern JavaScript. A good one-page resource for React is the React Handbook.

Install your Tools

Install NodeJS. If you already have, be sure it's up to date. You need npx from the new versions of Node to run the create-react-app script.

Install the React Devtools for your browser. create-react-app does a lot of repackaging of your source HTML and JavaScript for deployment. The HTML and JavaScript the browser gets is very different from what you wrote. The devtools read metadata generated by create-react-app so that you can interact with your original source in the browser debuggers.

Do the Quick, React! Tutorial

Do the Quick, React! tutorial. This introduces the essential JavaScript and React programming concepts needed to get a React app going on a clean solid basis. It includes functional components, stylng, fetching data, interactive UI state handling, working with the Firebase real-time database, and authentication with Firebase.

It follows the Agile approach of implementing applications in very thin end-to-end vertical slices, using the React development process described in the Thinking in React tutorial:

  • Create the data or state.
  • Implement and test static display of the data or state.
  • Finally, implement user interactions.

A slice is a thin bit of your app, including user interface and back end. Do not do all front-end first, or all back-end first. What you choose to do in each slice is largely up to you, but it should something that either provides the most value for the end user or reduces the most risk on the technical side.

The App

This activity challenges you to build a moderately complicated shopping cart app.

If you are doing this in one of my courses, I will provide spreadsheet for reporting progress, via Canvas. This is where you will enter links to your repo, your app's public site, and the various commits along the way that commplete each subtask.

Your goal is to build an app very similar to this shopping cart app.

Play with this app briefly to see how it works. Some parts of it are obvious. Some parts are not, e.g., how to remove an item from the cart, and what text like "9 x $1.21" means.

Backlog

Here is the prioritized backlog of user stories for your app, broken down into user-testable releases.

Release 1: Catalog page

  • Shoppers can see a catalog page of all available T-shirts with names, prices, sizes, and pictures.

Release 2: Basic shopping

  • Shoppers can add a shirt to their shopping cart by clicking on one of the size buttons.
    • The same shirt and size can be added more than once.
  • Shoppers can see what they've selected so far and the total cost.
  • Shoppers can remove a shirt from their cart.

Release 3: Persistence and Inventory

  • A shopper can log in so that their shopping cart is automatically saved and available from any device.
  • A shopper always see what is currently in stock.

Create your app repository

Use create-react-app to create an empty shell for your shopping cart app.

npx create-react-app new-shopping-cart

Test that your empty app runs.

cd new-shopping-cart
npm start

The React web page should open in your browser.

Stop the server.

Create a repo for your app on github. Commit your code to it.

Put your name and a clickable link to the Github repo in the Learn React Report spreadsheet (link on Canvas).

Commit often to your local repo. Commit to github when something new works, but never break master.

Go public

Even though you don't have anything real yet, set up a public host for your project, so that you can user test and show your client, e.g., me., where you're at.

There are many web hosting services for dynamic web apps running NodeJS, such as Firebase, Heroku, and AWS. We’ll give instructions for Firebase. It provides three services for free that we need here: hosting, authentication, and a database.

Create an account at Firebase, if you don’t already have one.

Create a Firebase project at Firebase. This will be the back-end for your app. You can call it anything, but choose something the same as or close to your app name.

Add a database to the Firebase project. Be sure to choose the real-time database, not Firestore. Create it in test mode, to avoid permission issues.

In a terminal window on your machine, install the Firebase CLI globally with

npm install -g firebase-tools

Switch into your local app directory and initialize your app's Firebase configuration:

firebase init

Choose both hosting and the real-time database.

More on setting up Firebase apps.

When initialization finishes, build and deploy

npm run build
firebase deploy

This will assemble your app and deploy it to Firebase.

If deployment fails, it's probably because you are not logged into Firebase. Do firebase login and try to deploy again.

Open the URL for your app generated by Firebase. Verify that the app is working, images and all.

Create and commit your working app to github. Put your public URL on the report spreadsheet.

From this point on, your process for closing each task will be

  • Code until the app works locally, using npm start.
  • Build with npm run build, deploy with firebase deploy, and test your public version
  • Commit to Github.
  • Enter the link to the commit into the progress report spreadsheet.

Implement a static page

Always start an app by building the static view. This will give you something you can test and show to users, and provide the basis for designing the interactive parts. There are several steps to creating a static catalog page.

  • Create the data to display.
  • Sketch the user interface, in terms of logical components.
  • Implement the components as data-driven React components and HTML

Get and test your data

Always use realistic data. No dummy "lorem ipsum" stuff. Keep the amount of data small but complete enough to demonstrate the user stories you want to implement first.

Organize your data into a logical JSON structure.

Put the data in an easy to access and deploy place. For early testing of static data, you can use a JSON text file in the application's public folder.

To kickstart this project, get the file products.json. It's a modified version of the one in Ribeiro's shopping cart.

Put it in public/data/products.json in your app directory.

Replace the entire contents of the default src/App.js file with

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

const App = () => {
  const [data, setData] = useState({});
  const products = Object.values(data);
  useEffect(() => {
    const fetchProducts = async () => {
      const response = await fetch('./data/products.json');
      const json = await response.json();
      setData(json);
    };
    fetchProducts();
  }, []);

  return (
    <ul>
      {products.map(product => <li key={product.sku}>{product.title}</li>)}
    </ul>
  );
};

export default App;

If you don't know what every step of the above code does, re-read Quick,React!

Run your app. If everything is set up correctly, instead of the React logo, you should see a plain list of the product titles. When this works, you're ready to do a real display of that data.

Build, deploy, and run your app on the public host. It should look the same as your local version.

When your app is working on the public site, commit the code to github, and update the progress sheet.

Outline your user interface

Create a sketch of the UI, comparable to that in the example app, using pencil and paper, a sketching tool, or an annotated screen grab of the current UI.

Study the example on Thinking in React:

On your UI sketch, outline what you think would be appropriate containers and components.

MVP 1: A static catalog page

Your first testable product would be a page showing all the T-shirts, including pictures, names, prices, and sizes. Assume for now that all sizes of all shirts are in stock.

Put the images from the example app repository under the public folder. For example, if you put them in public/data/products/, then <img src="data/products/12064273040195392_2.jpg"> will be the thumbnail image for the T-shirt with the product ID 12064273040195392.

There is a lot to build here, especially if you are new to React. The way to get things done is to be agile: work in tiny testable slices. The following would be good slices. They can be done in almost any order:

  • Show a grid of "cards" with the product titles
  • Show the pictures of the shirts
  • Show the descriptions of the shirts
  • Show the prices of the shirts
  • Show four size buttons, S, M, L, XL for each shirt

Install and start using a style framework for your very first slice.

As soon as a slice works on your public host, commit to github, and update the report spreadsheet.

Add interaction

Adding the ability to add items to a cart means adding what React calls state. Read carefully what Thinking in React says counts as state. State has to do with the local user interface, not the state of the application. State in React does not include static data, nor anything that can be calculated from other bits of state.

The trick to implementing state is to first create the state variables with different values, and update your static display code to show the state appropriately. Don't try to implement state change until static state display works.

Identify the necessary state

In this case, there are two pieces of UI state:

  • Where the shopping cart is visible or not.
  • What's in the cart

Implement static state diplay

Add state using the useState() hook. Define the new state in the smallest containing component possible.

This makes the app easier to maintain and modify, and reduces how much prop passing you have to do.

Do states in tiny slices, e.g., first do the state variable for whether the cart is open or closed. Test by initializing the variable to true and false, and make sure the shopping is visible only when it's supposed to be. After that works, add the state variable for the list of items selected so far. Be sure to be able to handle more than one of an item. Initialize with different lists and verify that the cart shows the right list, and, eventually, the right total cost.

More on state.

When your app is working locally and on the public site, commit to github, and update the progress sheet.

Implement interactive state

This is where you add interactivity to your app. There are three primary user actions: viewing the cart, adding items to the cart, and removing items from the cart. That's a good order to do them in.

Do opening and closing the cart first. Clicking on the cart icon or any "buy" button should open the cart. Click on the close button should close the cart.

See React Old and New for how to do this.

When your app is working locally and on the public site, commit to github, and update the progress sheet.

Now implement adding items to the cart. Adding an item should automatically open the cart and update the total.

When your app is working locally and on the public site, commit to github, and update the progress sheet.

Add removing items from the cart.

Add a control for removing an item. If more than one of a specific item has been selected, the count should be decremented by one.

When your app is working locally and on the public site, commit to github, and update the progress sheet.

Fetch from a database

Up to this point, all the data has been static. Most interesting web applications need to keep dynamic data on a server, whether it be game scores, comment posts, or whatever.

For this project, two examples of dynamic data would be

  • the inventory of shirts in stock
  • the user's shopping cart

Start with the inventory, since it doesn't require implementing authentication.

Show availability in your user interface

Use useState() to add a state variable for inventory, with an initial small inventory of just a few shirt sizes.

Copy some of the data from inventory.json.

Update your product display code to only show size buttons for available sizes of each shirt. If no sizes are available, show a non-clickable "out of stock" text message.

Update your code to take into account the items in the shopping cart. Verify that when you select the last available size of a shirt, that size becomes out of stock. If you remove that size shirt from the cart, it becomes available agint.

When your app is working locally and on the public site, commit to github, and update the progress sheet.

Add a database

The Firebase Real-time Database (Firebase RT) is a very simple cloud database. Everything is stored as one big JSON object. Every subitem should be indexed under a stable unchanging sequences of keys. Both products.json and inventory.json are Firebase-compatible.

Initialize the inventory

Initialize the database with inventory.json file, using the Firebase console import command.

Importing a JSON file completely replaces all data. Only do this to initialize or reset your database.

Fetch inventory from the database

Add code to get the inventory data from Firebase and store it in your local inventory state variable.

See this section of the Quick, React! tutorial.

Verify that all shirt sizes not listed in the inventory file no longer appear on the catalog page. Verify that adding and removing items from the cart still updates the available sizes appropriately.

When your app is working locally and on the public site, commit to github, and update the progress sheet.

Add authentication

Many developers do user accounts and login first. That's a terrible idea. There is nothing to be learned about your app idea from implementing login.

But once you have a working prototype that suggests your app is a good idea, you will probably want to keep track of users. Shopping is clearly an application that needs to know who someone is when they checkout.

Add user identity to your app

Add the ability to sign up and log in.

Firebase supports multiple authentication options, e.g., email, Facebook, and Google. Add at least one form of authentication to your app. Google and email are quite simple, thanks to Firebase.

If the user is not logged in, your interface should show buttons to log in or sign up. If the user is logged in, your interface should show who is logged in and a logout button.

More on authentication with Firebase.

When your app is working locally and on the public site, commit to github, and update the progress sheet.

Add database updating

Add persistent shopping carts

Change your app so that the shopping cart for a logged-in user is automatically saved under the carts/userid key in the database, whenever the cart changes.

Verify that you see the data appear on the Firebase console.

Add code so that when a user logs in, any saved items are added to the current shopping cart. Don't replace the current cart. The user may have selected some items before logging in. The user can always delete old items if they want to.

Careful! The logic for this has many edge cases. For example, whatever the user has selected plus the old cart may not be valid, given the currently available stock. When that happens, the cart has to be adjusted and a message appear telling the user what happened. This message should display until the user closes it.

When your app is working locally and on the public site, commit to github, and update the progress sheet.

Add stock updating

Change your app so that when a user checks out, any items in their cart are removed from the inventory on the database.

This may cause other carts to contain items no longer available. Verify that pages of other users properly update and alerts those users if any items in the cart had to be removed. Test by logging in as two different users, on two different browsers. Set up the shopping carts such that hen one user checks out, the other user's cart has to be adjusted.

When your app is working locally and on the public site, commit to github, and update the progress sheet.

© 2019 Chris Riesbeck
Template design by Andreas Viklund