Skip to content

Exercise 13 : ShoppingList

Purpose and learning process

In this exercise you will learn the basics of the PWA development with React:

  • create a React app with PWA support
  • modify index.html
  • modify manifest.json
  • basic use of chrome development tools with PWA
  • save and load data from Firebase
  • use 3th party library to display shopping list data

Now it is time to use React to create PWA ShoppingList. In this exercise you will learn how to use Firebase to store your mobile application data. You will create a shopping list application, which will store shopping items to Firebase.

Example screenshots and video

Demo app is a ShoppingList app, which stores shopping list items to Firebase.

ShoppingList 01 ShoppingList 02 ShoppingList 03

Example video: https://youtu.be/JyH5htuxpLU.

Setting Up Firebase

Firebase is Google's mobile platform that helps you quickly develop high-quality apps and grow your business.

Go to the homepage of Firebase and click on the Get started button and Create a new ShoppingList project.

!ShoppingList 04

You can enable (or not) Google Analytics for your Firebase project. Google Analytics is a free and unlimited analytics solution that enables targeting, reporting and more in Firebase Crashlytics, Cloud Messaging, In-App Messaging, Remote Config, A/B Testing, Predictions and Cloud Functions.

!ShoppingList 05

Choose or create a Google Analytics account used with this app.

!ShoppingList 06

Your project should be now ready for development.

!ShoppingList 07

Note

This app can be done without Google Analytics.

Read more: https://firebase.google.com/.

Add a database

Configure Firebase realtime database for your ShoppingList App. Click on Cloud Firestore badge or select Firebase Database in left side menu. Click Create database button.

!ShoppingList 08

Select the "Start in test mode" option and click on the next button. Now you are developing your application, so you should start in test mode. Test mode lets anyone, with or without authentication, read and write to your database. Although this mode is good for developing and debugging, you should always change to production mode when you're ready for production.

!ShoppingList 09

A final step is to specify Cloud Firestore location. You should select some location from Europe.

!ShoppingList 10

Finally your database is ready to use.

!ShoppingList 11

Add a sample data

Select your database and add a collection for a shoppinglist items. Select the + Add collection option, and create a name for collection. As highlighted in the tooltip, a collection is a set of documents, which contain a data. Use the items for creating a database of shoppinglist one item details.

!ShoppingList 12

And add a few sample data to the database. Use name as string and count as number fields. Click Add document to add more items to shoppinglist database.

!ShoppingList 13

Each shoppinglist items is a document in a database.

!ShoppingList 14

Create a new React project

Create a new React app with create-react-app @ GitHub. You’ll need to have Node 10.16.0 or later version on your local development machine (but it’s not required on the server).

Use npx, npm or Yarn to create your project.

1
npx create-react-app shoppinglist-app --template cra-template-pwa

Read more: https://create-react-app.dev/.

Move web app to PWA

index.js

The production build has all the tools necessary to generate a first-class https://web.dev/progressive-web-apps/, but the offline/cache-first behavior is opt-in only. By default, the build process will generate a service worker file, but it will not be registered, so it will not take control of your production web app.

In order to opt-in to the offline-first behavior, you should look for the following in their src/index.js file and use serviceWorker.register() to register Service Worker:

1
2
3
4
// 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.register();

As the comment states, switching serviceWorker.unregister() to serviceWorker.register() will opt you in to using the service worker and you now have a PWA.

Read more: https://create-react-app.dev/docs/making-a-progressive-web-app/.

index.html

Modify public/index.html to give a correct app name to your PWA. Replace correct <title>:

1
<title>Shopping List</title>

manifest.json

Modify public/manifest.json file to have correct name in short_name and name attributes:

1
2
"short_name": "Shopping List",
"name": "Shopping List",
You can use a default icons or create a new ones. if you want to replace the icons your app uses, remember place a new icons to public folder.

package.json

If you are planning to publish your app in some sub folder in your web server, then you should open package.json and add a following homepage attribute:

1
2
3
4
{
  "name": "shoppinglist-app",
  "homepage": "./",
  ...

Now static files are not looked from the webserver root. Of course you need to do this, if you are planning to publish your project to https://student.labranet.jamk.fi. You don't have access to student web root, you only have access to your own folder in student.

Test

Start your development server and test that your app is working so far.

Give a command npm or yarn start

1
yarn start

And you should see your app running in localhost.

!ShoppingList 15

Firebase library

Add a Firebase library to your project with a following npm:

1
npm install firebase

Connect the firebase project

In Firebase ShoppingList project’s homepage, you will notice three circular icons. The first two will contain code that will help you connect Firebase project to an iOS and Android apps. The third one will have code that will help you connect the ShoppingList project to a web app.

!ShoppingList 15

Click on it, register your app. Note apiKey, authDomain, databaseURL, projectId, storageBucket, messagingSenderId, appId and measurementId values. These values are unique for each user’s each project.

!ShoppingList 15

Add a following code to your App.js. Remember change your own data here.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
import { initializeApp } from "firebase/app";
import { getFirestore, collection, getDocs, addDoc, deleteDoc, doc } from 'firebase/firestore/lite';

const firebaseConfig = {
  apiKey: "your-data-here",
  authDomain: "your-data-here",
  databaseURL: "your-data-here",
  projectId: "your-data-here",
  storageBucket: "your-data-here",
  messagingSenderId: "your-data-here",
  appId: "your-data-here",
  measurementId: "your-data-here"
};

const app = initializeApp(firebaseConfig);
const db = getFirestore(app);

Load sample ShoppingList items using React Hooks. Import useEffect and useState.

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

Modify your App.js to load ShoppingList items from Firebase. You can use useEffect to fetch data from the database.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
function App() {
  // loading state
  const[loading, setLoading] = useState(true);
  // shopping list items state
  const[items, setItems] = useState([]);

  // load shopping list items
  useEffect(()=> {
    const fetchData = async () => {
      // get collection
      const dataCollection = collection(db, 'items');
      const dataSnapshot = await getDocs(dataCollection);
      // shopping list items: name, count and id
      const items = dataSnapshot.docs.map(doc => {
        return  { 
          name: doc.data().name, 
          count: doc.data().count, 
          id: doc.id 
        };
      });
      // set states
      setItems(items);
      setLoading(false);
    }
    // start loading data
    fetchData();
  },[]); // called only once

  // render loading... text
  if (loading) return (<p>Loading...</p>);

  // create shopping list items
  const sh_items = items.map( (item, index) => {
    return (<p key={index}>{item.name} {item.count}</p>);
  });

  // render shopping list
  return (
    <div className="App">
      {sh_items}
    </div>
  );
}

Test

Save and test your app. You should see your shopping list sample data in browser.

!ShoppingList 16

UI

Select for example MUI to create UI for ShoppingList app.

Install Material-UI's source files via npm.

Note

Remember that you can use any UI library in your exercise. Here a few examples:

Create UI with needed components and use some styles.

!ShoppingList 17

Add item to ShoppingList

Program add button to add a new item to ShoppingList database.

Add a new hooks to get item name and count from TextInput components and use onChange event in TextField's. Set default item name to "" and count to 1.

1
2
const[item, setItem] = useState("");
const[count, setCount] = useState(1);
1
2
onChange={e => setItem(e.target.value)}
onChange={e => setCount(e.target.value)

Add a new addItem function and call it from the add button's onClick event. A new item will be created and added to the database. Database call will return added item document. This document id will be stored to shopping list item, so it can be deleted later.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// add a new item to data base and shopping list items
const addItem = async () => {
  // create a new shopping list item
  let newItem =  { name: item, count: count, id: '' };
  // add to database
  let doc = await addDoc(collection(db, 'items'), newItem);
  // get added doc id and set id to newItem
  newItem.id = doc.id;
  // update states
  setItems( [...items,newItem]);
  setItem("");
  setCount(1);
}
1
onClick={() => addItem()}

Delete item from ShoppingList

Add a new deleteItem function and call it from the shopping list items remove button's onClick event. First delete item from database and then from the items state.

1
2
3
4
5
6
7
// delete item from database and UI
const deleteItem = async (item) => {
  deleteDoc(doc(db, "items", item.id));
  // delete from items state and update state
  let filteredArray = items.filter(collectionItem => collectionItem.id !== item.id);
  setItems(filteredArray); 
}
1
onClick={() => deleteItem(item)

Test

Test your app in localhost. It should work now.

Remember

  • test with mobile size - use 'Toggle device tool' in localhost

!ShoppingList 18

  • PWA test (publish first to your https-server)

!ShoppingList 19

Note

Yes - this is not optimized PWA. You can learn how to optimize your PWA - for example in research assignment.

Build and publish

Create a new build and publish your files to server. Remember use HTTPS connection.

Test and push

Test your application in your mobile phone browser (chrome@Android, safari@iOS), take screenshots and commit/push your project and screenshots back to JAMKIT/GitLab.

ShoppingList 01 ShoppingList 02 ShoppingList 03

Note

Note how app is running without location bar in PWA (lauched from icon created in home screen).

Final words

https://firebase.google.com/ is excellent tool for developers.

Firebase Pros & Cons

  • Email & password, Google, Facebook, Github, etc... authentication
  • Realtime data
  • Ready-made api
  • Built in security at the data node level
  • File storage backed by Google Cloud Storage
  • Static file hosting
  • Treat data as streams to build highly scalable applications
  • etc...

Note

You should give it more time to learn it better - for example in research assignment.

Push to GitLab

Test your application in emulator, take screenshots (add those to your project folder) and commit/push your mobile-exercises repository back to JAMKIT/GitLab. Remember move your exercise/issue ticket from Doing to In Review in Issues Board and write your learning comments to issue comments.