Categories
JavaScript React

Course Review: ‘React for Beginners’ by Wes Bos (2020)

Having gone through a video course of his some years ago, Wes Bos made a lasting impression on me as someone that could make learning to code really enjoyable and explain relatively complex concepts in an easy-to-understand way. So, when I embarked on this journey to learn React and wanted to go beyond freeCodeCamp’s intro course, I knew where to go next: his React for Beginners course.

Note: after purchasing the course and joining Wes’ dedicated Slack channel for the course, I saw a couple of mentions of this course needing to be updated. I started to worry that what I was learning was out-of-date already, so I asked for some clarification. A couple of helpful fellow members of the group summarised it for me:

  1. In Video 12 and 13, value.value becomes current.value.
  2. The FireBase UI has been updated. So has Twitter’s developer/API area.
  3. Hooks have since been introduced to React which might’ve changed the way the app would be built if starting from scratch.

Altogether, these are minimal issues and the entire course remains relevant and valuable. Better still, having purchased the course you get access to the updated version when Wes re-records it which is great.

Table of Contents

  1. Video 1: Introduction, Tooling and Editor Setup
  2. Video 2: Thinking and Understanding React Components
  3. Video 3: Creating our First Components
  4. Video 4: Writing HTML with JSX
  5. Video 5: Loading CSS into our React Application
  6. Video 6: Creating our application layout with components
  7. Video 7: Passing Dynamic data with props
  8. Video 8: Stateless Functional Components
  9. Video 9: Routing with React Router
  10. Video 10: Helper and Utility Functions
  11. Video 11: Events, Refs and this Binding
  12. Video 12: Handling Events
  13. Video 13: Understanding State
  14. Video 14: Loading data into state onClick
  15. Video 15: Displaying State with JSX
  16. Video 16: Updating our Order State
  17. Video 17: Displaying Order State with JSX
  18. Video 18: Persisting our State with Firebase
  19. Video 19: Persisting Order State with localstorage
  20. Video 20: Bi-directional Data Flow and Live State Editing
  21. Video 21: Removing Items from State
  22. Video 22: Animating React Components
  23. Video 23: Component Validation with PropTypes
  24. Video 24: Authentication
  25. Video 25: Building React for Production
  26. Video 26: Deploying to Now
  27. Video 27: Deploying to Netlify
  28. Video 28: Deploying to an Apache Server
  29. Video 29: Ejecting from create-react-app

Video 1: Introduction, Tooling and Editor Setup

To kick things off, Wes introduces us to the project files, the terminal, code editor and more.

The project, ‘Catch of the Day’, is an online store selling seafood which allows you to add multiple items to a basket, as well as live updating of inventory. The files can be downloaded from GitHub (https://github.com/wesbos/React-For-Beginners-Starter-Files).

Wes then takes us through the package.json file, explaining the different parts of the file;

  • devDependencies – packages that are needed for development of the project, such as preprocessors
  • dependencies – packages needed to run the actual live project
  • scripts – ‘shortcuts’ to run series of commands, speeding things up

Running the following command in the terminal will install each of the dependencies above:

npm install

Running the following command launches one of the scripts listed in the package.json file:

npm start

This compiles all of the code and launches a new tab in your browser with hot reloading enabled. Hot reloading makes the development process so much easier as you can see the impact of your changes almost immediately. Whenever a change is made to a file in the project – files that are ‘watched’ – the browser will automatically reload the project to show the changes.

Video 2: Thinking and Understanding React Components

In this video, we get our first proper look at React.

React is made of components. They look a lot like custom HTML elements, but are much more powerful.

Wes shows us how we can view the React code used by websites – such as Facebook and Instagram – as React components, instead of just pure HTML.

To do this, you download the React Developer Tools extension for Chrome.

Launch the Developer Tools window in Chrome and click on the Components tab that should appear. Click on a component to see its ‘Props’ and ‘State’ (essentially just information stored by that component, but can be tied it how it looks).

You can see React in action by editing a prop or a state’s value and watch how it changes what’s displayed on the front-end (make sure you pick one that’s tied to something visual!).

Remember, the key thing about React here is that you aren’t running any JavaScript to make those changes and you’re not directly editing the HTML either. Instead, you are changing the underlying ‘record’ of information and React is reacting to it automatically.

Video 3: Creating our First Components

We’ve mostly been getting prepared and looking at examples of React at work, but now we’re getting into coding our own!

We get started by working within the index.js file and Wes explains how this project’s workflow will bundle all of the JavaScript together – React, our dependencies, and our custom code – into a single bundle.js file which is injected at the end of the page.

Importing the React Module

To get started using React, we need to first import the React package or module. To do this, we just need to include the following line:

import React from "react";

This will look for the React package in the node_modules folder and make it available to us in our project.

Creating a Component

We use classes to create components so that we can re-use them. We can do it like this:

class StorePicker extends React.Component {
  // component code goes here
}

Every single class in React needs at least one method inside it called render. Unsurprisingly, this tells React what to output or display. This render method should return only one element, like below:

class StorePicker extends React.Component {
  render() {
    return <p>Hello!</p>
  }
}

Rendering React Components to the DOM

Now, in order to actually render our React component to the DOM, we need to import the render method from the react-dom package, so we need to write the following:

import { render } from 'react-dom';

So, we’ve imported React and the ability to render React components to the DOM. We’ve also got a component ready to render. Here’s how we render it to the DOM:

render(<StorePicker>, document.querySelector("#main"));

Keeping Components Organised

So that we don’t end up with either one huge file with lots of components in it, or alternatively a messy project folder where components mix with the main HTML, CSS and JS files, we should create a dedicated components folder. So, for the StorePicker component we’ll create a StorePicker.js file in the new components folder and cut and paste the component into there.

Tip: we’ll always need to import React to every single component, so remember to add it to the top of the file.

Separating components into different files means we need to make sure any component we use in a file is made available. To do this, we’ll need to do two things:

  1. Export the component itself
  2. Import the component where we want to use it

In order to ‘surface’ a component (or function) for other files to use, we first need to export the component:

export default StorePicker;

We can then import the component elsewhere using:

import StorePicker from './StorePicker';

Notice that the location is a relative path as it will originally look into the node_modules folder.

Video 4: Writing HTML with JSX

Typically, writing HTML inside of JavaScript is a real pain. With ES6 it’s a little bit easier with template literals. However, with React we have JSX which allows us to quite easily mix HTML and JS.

Even though JSX isn’t compulsory, Wes shows us the React.createElement method and highlights the drawbacks (such as difficulties when nesting elements). JSX is the recommended way to render the contents of a component.

One of the differences with JSX comes when you want to add a CSS class to an element. In JSX, you need to use className.

If you want to return multiple lines in your component’s render method, you just need to return your JSX inside of brackets like this:

class StorePicker extends React.Component {
  render() {
    return (
      <div>
        <h1>This is a title!</h1>
      </div>
    )
  }
}

Always Return One Element

We mentioned this earlier, but we can’t return adjacent, sibling elements. We have to return just one element.

Many developers use <div> elements, but this can mean a complex app ends up with a nested nightmare of <div>‘s. React 16.2 introduced fragments, which React interprets as a sort of ‘non-element’ we can wrap around our elements. This looks like:

<React.Fragment>
  <p>Sibling 1</p>
  <p>Sibling 2</p>
</React.Fragment>

However, if we import React.Fragment explicitly we can just use <Fragment> like so:

import React, { Fragment } from 'react';

...

<Fragment>
  <p>Sibling 1</p>
  <p>Sibling 2</p>
</Fragment>

Commenting

Writing code comments in JSX is also slightly different. What we need to do is use curly braces which tell React to interpret the contents as regular JS, then use JS block commenting syntax like the below:

{ /* This is a JS comment in JSX */ }

It’s also worth noting that comments must go inside the returned JSX element and not be the very first line in the return statement, otherwise it will throw an error.

Video 5: Loading CSS into our React Application

In terms of getting our CSS into the project, it’s pretty straightforward.

On the one hand, we could write CSS on a per-component basis and couple it closely with the components themselves or, alternatively, we can just import a CSS file directly into our index.js file like you might do normally from within the <head> of your HTML page.

For simplicity, we start with the second option; importing the project-wide CSS file. To do this, we add another import statement to the index.js file (the root or parent file in this project):

import './css/style.css';

Once we’ve done this and saved the file, the pre-defined workflow (Webpack, etc) that came with the create-react-app foundation that the project is based on will handle the CSS build, injection and hot-reloading so we’re good to go!

Video 6: Creating our application layout with components

With the initial ‘setup’ complete and armed with a basic understanding of React components, we get started with laying out the main components of the project.

We start by creating an <App> component to house all of the other components. This means that within the index.js file we only need to render the one component in our React DOM render method.

We then create a <Header> component – being sure to import react at the start and export the component at the end – and use this as the basis for additional <Order> and <Inventory> components. These represent the three main sections of the app.

Video 7: Passing Dynamic data with props

Props and State are fundamental to React.

Wes explains that props (which stands for properties) are similar to HTML attributes in the sense that they tell a component (or element in the case of HTML) what to display. They give components the information they need.

State, on the other hand, is where the data lives. It is where the current state of the components in your app are stored.

Props are essentially what makes a React app dynamic.

There are two key parts to using props:

  • Defining a prop and passing the data when instantiating a component from the parent component
  • Displaying the prop within the child compontent itself

When it comes to defining a prop, there are no pre-existing props – you can name them pretty much any way you like.

When passing through data, if it’s a string you can just put it in quotation marks. If it’s anything other than a string, put it into curly braces.

In our Catch of the Day project, Wes asks us to add a prop called “tagline” to the <Header> component like this:

<Header tagline="Fresh Seafood Market" />

In terms of displaying props, we simply refer to the property that we defined like this:

<p>{this.props.tagline}</p>

In the code above, “this” refers to this specific instance of the component. Considering we can instantiate as many <Header> components as we like, we could pass through a different tagline prop value for each one and by using this we would always be referring to that specific instance of the component.

Finally, Wes shows us a useful little tip within your browser’s dev tools.

In the regular ‘Elements’ tab that shows the HTML, you can select an element and then type $0 in the console to show that element which you can then explore as a node or object.

With React, you can do the same – except in this case it’s with $r. It will output the component along with all of the ‘internal’ information for that component, such as it’s props.

Video 8: Stateless Functional Components

The components that we’ve created so far have been React ES6 Class components that have simply included a render method to output something. A simplified example of the Header component would be:

class Header extends React.Component {
  render() {
    return (
      <span>{this.props.tagline}</span>
    );
  }
}

Wes takes us through some of the code in the finished app and it becomes clear that components can get a lot more complex. The components we’ve created are relatively simple things – and this is the point…

Introducing Stateless Functional Components

Components that don’t do anything – that mainly just display information that is passed to them – represent a perfect opportunity to simplify further. We would convert these into stateless functional components.

A stateless functional component looks like this in ‘regular’ JS:

function Header(props) {
  return (
    <span>{props.tagline}</span>
  )
}

Compared to the React Class style of component, it is quite a bit simpler from the outset.

Notice how when moving to a stateless functional component and writing regular JS within curly braces, we no longer have to use this when displaying a prop. This is because the props have been passed in as the argument.

We can actually go even further by using ES6 syntax and converting the regular JS function into an arrow function with an implicit return (no need to use return as it’s implied by the opening brackets after the arrow). The result looks like this:

const Header = props => (
  <span>{props.tagline}</span>
)

It’s worth bearing in mind that you’ll need brackets around props if you have more than one argument in the function.

A further option we have to tweak this function is to destructure props into their own variables. This means we no longer have to use props when referring to a prop we want to display.

const Header = ({tagline}) => (
  <span>{tagline}</span>
)

So, what we’re doing here it taking the value of a property of an object and placing it in a variable, making it a more simple reference.

So, not only have we brought 7 lines down to just 3, but arguably we’ve made the JSX that we want to render shorter (and thus quicker to write, potentially less prone to syntax errors, etc) as there’s no need to use this.props.tagline when we can just use tagline.

So, the lesson here is; if you have a component that just gets passed some props and returns some JSX then a stateless functional component should be considered. It makes code a bit more legible/easier to understand and your app will potentially benefit from improved performance, too.

Video 9: Routing with React Router

Wes seemed to run through this one at the speed of light compared to the previous videos, so let’s break it down…

We’ve been building an app which we know has two different ‘screens’ or pages that feature a single main component; the Store Picker and the App. We need each of them to be available on different URL paths so that we can navigate between them. It’s worth bearing in mind we also need to cater for 404 Not Found errors when a user arrives at a non-existent page.

To do this, we need a router. There are several available, but the main ones are React Router and Next.js. These packages provide us with several components that allow us to do all of the above.

Wes’ recommendation is to create a new Router component of its own (in its own .js file) and then render this instead of either <StorePicker> or <App> from the index.js render method.

This is where it becomes clear; the Router component is like arriving at a junction and your satnav telling you which way to turn. We get to the junction (the Router component) and decide where we go from there (<StorePicker>, <App> or elsewhere).

To get started, we’ll want to import the bits that we need like so:

import { BrowserRouter, Route, Switch } from 'react-router-dom';

We’ll then create a stateless functional component called Router and start using the imported components:

const Router = () => (
  <BrowserRouter>
    <Switch>
        <Route exact path="/" component={StorePicker} />
        <Route path="/store/:storeId" component={App} />
        <Route component={NotFound} />
    </Switch>
  </BrowserRouter>
);

According to the creators of React Router, “A <Switch> looks through its children <Route>s and renders the first one that matches the current URL.” It’s a bit like a regular JavaScript switch statement.

Simple enough. Each <Route> is a possible path to take depending on the conditions. The conditions in this instance are the path attributes/arguments we’ve supplied and the qualifier in the first case (exact).

The final part to mention is the inclusion of :storeId in the second <Route>. This part acts as a sort of wildcard that will run on any matching path, e.g. /store/123 or /store/abc.

The clever bit here is that :storeId gets stored as a param prop in the component that the <Route> renders (in this case <App>). If we go to a matching URL that’ll render the <App> component (e.g. /store/123) and open up React Dev Tools, we can see that our <App> component now includes storeId as a prop under match and params.

Although Wes doesn’t show us in this video (I’m sure it’ll come later), it’s clear that we can now use storeID in our app as it’s a prop. For instance, we could display it by using:

<span>Store ID: {match.params.storeID}</span>

Video 10: Helper and Utility Functions

In this video, Wes explains how he uses some functions throughout the app that don’t necessary need to be React components. For example, a function to convert currency or get a random string. These are ‘helper’ functions and they sit outside of the components folder in a helpers.js file, alongside index.js.

export vs. export default

Wes uses this opportunity to point out the difference between export and export default.

When importing from another JS file, we can either name the specific function or we can get the default. If we want to get the default, we just name the exported function:

import React from "react"

The capitalised React is the important part here – it represents the default function.

To import specific functions from a file (that isn’t the default), you’ll use curly braces for the name. Here’s the example from the React Router lesson:

import { BrowserRouter, Route, Switch } from "react-router-dom";

As you can see BrowserRouter and the other functions are specifically imported here, rather than the whole package.

<input> value in React

This lesson also teaches us an important ‘quirk’ with React/JSX and <input> elements.

When it comes to setting a value of an <input>, we can’t just do what we’d assume:

<input type="text" value="hello" />

This throws an error. Much in the same way we must use className instead of class on elements to add CSS classes, we need to use defaultValue instead of value on input elements. So, a correct example would be:

<input type="text" defaultValue="hello" />

Now, connecting this to the whole point of this lesson – the introduction of the helper functions – we could use one of our imported functions like so:

import { getFunName } from "../helpers";

// setup of the component, etc...

<input type="text" defaultValue={getFunName()} />

Video 11: Events, Refs and this Binding

Events in React are written similarly to regular JavaScript or jQuery, except that they’re done inline.

Whereas in JS you might created a button, add a class to it, then use that class as a selector in a function and create event listeners, etc. In React it’s more tightly coupled with the component.

In the background, they’re wrapped in something called a SyntheticEvent which ensures events work across browsers and are consistent.

Example React Event

Here’s an example of a very simple event which calls a function in a component:

// setup of the component, etc...

handleClick() {
  alert("Hey!");
}

// render method, etc...

<button onClick={this.handleClick}>Click me!</button>

First, we’ve added a camelCase event (“onClick”) to the element and then given it a function to run (“this.handleClick”).

As you can see, it’s slightly different from how you might do an inline JavaScript event in regular HTML. We’ve put it in curly braces so that React interprets it as a regular JS function, and we’ve also not included any brackets after the function name to hold any arguments. If we do this, it’ll be run automatically on loading rather than when the event is triggered.

Submitting the Store Picker Form

If we try submitting the form we have at the moment in our Catch of the Day app, it’ll just refresh the page. This is because if we don’t declare where the form should submit to, by default it will submit to the page itself. As we haven’t got any server-side code to handle the submission or return some instructions, it just refreshes the page.

So, what we need to do is attach an onSubmit event to the form and tell it to run a custom function (a method of our component) that’ll define what to do on submission. The element with the event looks like this:

<form className="store-selector" onSubmit={this.goToStore}>

In terms of the method, we need it to do three things:

  1. Stop the form from submitting
  2. Get the value of the <input> element that holds our store name
  3. Load the next page

Preventing the Form From Submitting

As we’re dealing with regular JS in the component (as opposed to JSX in the component’s render() method), we deal with preventing events in the same way we normally would. Here’s our custom goToStore method:

goToStore(event) {
  event.preventDefault();
}

Accessing Input Values

A quick tip from Wes; we generally don’t want to touch the DOM directly. This means we shouldn’t get the value of the input by doing something like document.querySelector(‘input’) or $(‘input’) in the case of jQuery. Instead, there’s a couple of other React ways to do it:

  1. Refs (which does touch the DOM, but let’s go with it)
  2. Syncing the input with State
Refs

Let’s start with refs. Refs are just references and they involve creating a new property to store the reference, like so:

myInput = React.createRef();

We then make the ‘connection’ to the reference above by adding a ref attribute to the input element like this:

<input ref={this.myInput} ... />

Great! We now have a reference to the input element and would expect to be able to access its value directly from within our new function. Something like:

goToStore(event) {
  event.preventDefault();
  console.log(this.myInput);
}

Well, not so fast!

this Binding

All of the methods that are part of React – such as render() and lifecycle methods like componentDidMount() – know what “this” is as they come directly from the React.Component class.

All custom methods – like goToStore() we created above – are not bound by default, so we need to bind “this” to the component explicitly.

One way to do this is to add an ES6 constructor and do the binding in there:

constructor() {
  super();
  this.goToStore = this.goToStore.bind(this);
}

Alternatively, we can turn our custom method into a property of the component by using an ES6 arrow function:

goToStore = event => {
  event.preventDefault();
  console.log(this.myInput);
}

The second option is clearly shorter and cleaner as we’re making very few changes to convert our existing method, though it’s perhaps a little more nuanced and needs a reasonable understanding of ES6 arrow functions to avoid the pitfalls of syntax errors if you’re not used to them.

Note that console.log(this.myInput) does not give us the input value, just a reference to the element. Accessing the value is coming in the next video.

Video 12: Handling Events

This video is pretty much a continuation of the last, so we’ll get back into accessing the value of an input element.

Getting the Input Value

We started with this.myInput as a reference to the input element. To access the value property, we need to use current.value (as opposed to value.value that Wes writes in the videos). So, the code will look like this:

goToStore = (event) => {
  event.preventDefault();
  console.log(this.myInput.current.value);
}

With the second of the three things we wanted to accomplish in the last video now complete (get the value of the element that holds our store name), we now need to load the store page URL using that store name.

pushState

To load a new URL without refreshing the page and losing the data we have (the store name, for example), we are going to use pushState and React Router.

If we use React Dev Tools to view our StorePicker component, we’ll see what we saw in Video 9 when we were looking at the :storeId param in the match method. The other props include history and location which are available because the StorePicker component is a child of the Router component.

Inside the history prop is a push method – this is the one we want!

So, first, let’s store the store name in a variable like this:

const storeName = this.myInput.current.value;

Then we’ll use that variable to push the new URL path within an ES6 template literal:

this.props.history.push(`/store/${storeName}`);

The final goToStore method looks like this:

goToStore = (event) => {
  event.preventDefault();
  const storeName = this.myInput.current.value;
  this.props.history.push(`/store/${storeName}`);
}

Now, clicking the ‘Visit Store’ button will take us to a URL like /store/repulsive-quaint-elves and because that matches the Route condition in our Router component (<Route path="/store/:storeId" component={App} />) then the store page loads. Hooray!

Video 13: Understanding State

This video is all about State.

What is State?

State is an object that lives inside a component, that stores all of the data that that component (and maybe some of its children) needs. It can be seen as the single source of truth for all data relating to that component.

If we remember the tip that Wes shared near the start of this course – that we should avoid touching the DOM wherever possible – then it becomes clear that we need a solution like this. With vanilla JavaScript or jQuery we might normally put data into data attributes (i.e. <div data-uid="456" data-name="Jenny">) and use that to bridge the gap, but with React that’s not recommended. So, we have State.

Having that single source of truth means we can update the state once and then all of the related components in your app will update accordingly. That’s the magic of React!

Laying the Groundwork

In order to demonstrate state, Wes has us create the ‘Add Fish’ form in the Inventory component.

As with the Store Picker component, we create the form fields themselves in the render method and then create references to these various inputs. We then create a method (function) to handle the onSubmit event of the form which then creates a new fish object with properties to store each of the references.

This data only exists in this component and there’s no way other components can currently access it properly. We need a way to get it into state so that the values can be shares across multiple components (such as the Order tab).

You can’t pass data up, but you can pass data down. The solution here is to put this data into the parent App component.

Setting Initial State

The first thing that we need to do is set the initial state, essentially what does it look like when the component loads (or is mounted).

There’s two ways that we can do this. The first is using the constructor() method, like this:

constructor() {
    super();
    this.state = {
        fishes: {},
        order: {}
    }
}

The other (shorter) way is to setup the state object like this:

state = {
    fishes: {},
    order: {}
};

In the examples above we set the properties to empty objects. Wes recommends setting the value to the type they’ll be when they’re fed some data (i.e. if the “name” property will store a user’s name, set it to an empty string initially).

Getting Data Into State

In order to update the state that lives in the top-level App component, the methods that update the state need to live within that component. The AddFishForm doesn’t have access to the App component’s state directly, so what do we do?

State Setting Methods Live in the Parent

Within the App component we need to create the method (addFish) that will set the state there and then we need to pass that method down two levels so that the AddFishForm component can run it.

To set the state, we need to take a copy of the existing state first. We never want to change the state directly (called mutation) as this can cause unwanted side effects. We can make a copy using an object spread, like this:

const fishes = {...this.state.fishes};

The next thing we need to do is add our new fish that we created in our createFish method within our AddFishForm component to that new fishes variable. In this instance, Wes uses a timestamp to help create a new unique key for each of our fish items:

fishes[`fish${Date.now()}`] = fish;

The final thing we need to do is set the new fishes object to state using React’s in-built setState method like this:

this.setState({
    fishes: fishes
})

As an added bonus, if the property and the value have the same name, ES6 allows us to pass the name just once like below:

this.setState({ fishes })

Pass the State Setting Method Down to the Child Component

How do we pass information between components? That’s right. It’s the return of the prop!

First, we pass the method as a prop to the Inventory component:

<Inventory addFish={this.addFish} />

We then do the same again in the Inventory component, passing it as a prop to the AddFishForm component. One difference, though, is that the method now no longer exists in the component we’re creating as it did before, it’s turned into a prop. So instead of this.addFish we use this.props.addFish like this:

<AddFishForm addFish={this.props.addFish} />

Call the Method in the Child Component

Now that we have access to the addFish method that actually sits within the App component from our AddFishForm component, we just need to call the method when our form submission event occurs.

this.props.addFish(fish);

Video 14: Loading data into state onClick

In the last video, we learnt how to set the state via an the onSubmit event. In this one, we’re doing the same thing but with onClick.

Our goal here is to create a button that, when clicked, loads some example fishes into state so that it can be displayed to the user (this part being covered in the next video). This will save us time because we won’t have to manually create each fish in order to test our code properly.

To do what we need to do, we’re going to follow exactly the same steps:

  1. Create a method that sets the state (lives in the component where state is being set)
  2. We pass that method down to child components through props
  3. We call that method via an event trigger (in this case, onClick)

As a side note, at the moment we don’t have a ‘database’ in the traditional sense, so the sample fishes data currently lives as an object in a separate file, and it’s imported like this:

import sampleFishes from "../sample-fishes";

Video 15: Displaying State with JSX

In this video we’re looking at how to display the state that we set in the previous video. The task we have to complete is rendering each of the fishes that we set into state into a list. So, how do we loop through the fishes object rather than manually and explicitly creating each of the individual fish and pass them their specific props?

Well, it’s important to know (which we have probably assumed by now, anyway) is that JSX does not have any in-built logic. There are no conditionals (if / else) and there is no looping (for each). This kinda makes sense as it’s closer to HTML than it is to JS. What can we do, then? Just use JS, duh!

Vanilla JS to the Rescue

As fishes is an object, we can’t use a method like map to loop through it, so instead we’ll use the Object.keys method to give us all of the keys (in the key: value pairings that make up an object’s properties).

Object.keys(this.state.fishes)

This returns an array (["fish1", "fish2", etc...]), which we can then map like this:

Object.keys(this.state.fishes).map(key => <Fish>{key}</Fish>)

This will give us a list of our fish – awesome!

Loops & Keys

The app works, sure, but if you look at the console React will have logged an error like this:

Warning: Each child in an array or iterator should have a unique "key" prop.

The reason for this is that React needs to be able to quickly and accurately identify each unique element / component in order to run efficiently. The solution is simple, too; we just need to add a key prop to the components that we’re creating and set a unique value for each. As we know that each key is unique in our fishes object, we can just use that.

Object.keys(this.state.fishes).map(key => <Fish key={key}>{key}</Fish>)

Pass the Data as Props

Then to pass data, we’re going to want to use props like so:

Object.keys(this.state.fishes).map(key => <Fish key={key} details={this.state.fishes[key]} >Fish</Fish>)

One of the things I learned here was the simple fact of passing through the whole object like above (e.g. all of the data relating to fish1), rather than splitting each property out into a different prop like this:

Object.keys(this.state.fishes).map(key => <Fish key={key} name={this.state.fishes[key].name} >Fish</Fish>)

This keeps your code tidier and leaves it to the child component (Fish, in this case) to decide what it needs.

Render the Props

We can then render the props that we need, accessing any prop that’s part of the specific fish object (i.e. fish1) because we passed the whole thing.

<li className="menu-fish">
  <h3 className="fish-name">{this.props.details.name}</h3>
</li>

If we get tired of writing this.props.details.example repeatedly within our component, or it starts to make the code untidy, we could just put them in variables…

const image = this.props.details.image;
const name= this.props.details.name;

But we could go one step further and really go crazy by using ES6 destructuring to set multiple variables from an object at the same time like this:

const {image, name} = this.props.details;

Video 16: Updating our Order State

Although not strictly about order state, Wes kicks this video off by showing us a quick way of toggling the disabled status on a button.

Simple Toggle for Element

First, set a variable to be equal to a condition. In this case, it’s whether the component’s status prop is equal to available. If it does, it’ll evaluate to true. If it doesn’t, isAvailable will be false.

const isAvailable = status === 'available';

Then we want to use that boolean variable to determine whether or not a <button> element is disabled.

<button disabled={!isAvailable}>Add To Cart</button>

Not only will this disable the button, but we can also apply styles based on this status.

We also use the same boolean isAvailable variable inside an ES6 ternary operator to change the content of that element, too:

<button disabled={!isAvailable}>{isAvailable ? 'Add to Order' : 'Sold Out!'}</button>

Updating State

Getting back to the primary goal of this video, we’re looking at setting the order state when someone clicks the ‘Add to Order’ button underneath a fish.

We’ve already got the order property sitting in state, so we just need to create a function to handle updating it. To do this we setup an addToOrder function with three parts:

  1. Take a copy of state
  2. Either add the fish to the order or update the amount of a fish if it exists already
  3. Call setState to update our state’s order property

Taking a Copy of State

Nice and easy, we just create a ‘temporary’ variable equal to the current order object in state:

const order = { ...this.state.order }

Adding or Updating the Order

We then update that order variable by setting a property equal to the fish’s key (fish1, fish2, etc) which will either add 1 if it exists or create it and set it to 1:

order[key] = order[key] + 1 || 1;

Setting State

Finally, we set the state with setState and pass our updated order object:

this.setState({ order });

Calling the Function

Next, we need to hook our ‘Add to Order’ button up to this function so that when we click the button it calls the function.

First, a little ‘gotcha’. The Fish component (along with its ‘Add to Order’ button) is going to need know the key of the fish that we are adding to the order. Just because we can see it in React Dev Tools, doesn’t mean it’s easy to access!

Inspecting the component, it becomes clear that it’s not surfaced anywhere. So, we need to explicitly pass the key via another prop instead like this:

<Fish key={key} index={key} details={this.state.fishes[key]} addToOrder={this.addToOrder} />

This should then show in the list of props as expected.

Next we need to add the click event to the button and call the function, passing the key we just set in the index prop as the argument:

handleClick = () => {
  this.props.addToOrder(this.props.index);
}

// the below is inside the return statement

<button disabled={!isAvailable} onClick={this.handleClick} >{isAvailable ? 'Add to Order' : 'Sold Out!'}</button>

That should work!

As always, though, considering this is a fairly simple and straightforward situation, we can implement the function directly in the onClick event instead.

<button disabled={!isAvailable} onClick={() => this.props.addToOrder(this.props.index)} >{isAvailable ? 'Add to Order' : 'Sold Out!'}</button>

Video 17: Displaying Order State with JSX

The focus of this video is to implement the logic and rendering of the Order component.

Passing Complete State to a Component

First, a quick tip.

If you want to pass the entirety of state to a component, instead of individually passing each prop and giving it a name like this…

<Order {fishes={this.state.fishes} order={this.state.order}} />

…we can instead use the ES6 object spread syntax to do exactly the same thing, like this:

<Order {...this.state} />

Wes highlights that this is kinda “lazy” and we really want to be making components very modular and know exactly what we’re passing through. Fair point!

Getting the Total Price of the Order

Next on the agenda is displaying a total cost for all of the fishes added to the order. The end result is below and I’ll explain after:

const orderIds = Object.keys(this.props.order);
const total = orderIds.reduce((prevTotal, key) => {
  const fish = this.props.fishes[key];
  const count = this.props.order[key];
  const isAvailable = fish && fish.status === 'available';
  if(isAvailable) {
    return prevTotal + (count * fish.price)
  }
  return prevTotal;
}, 0);

So, what is this block of code doing?

  1. Gets the IDs of the fishes added to the order and stores them in an array called orderIds by using the Object.keys method.
  2. Uses the reduce method on that array to iterate through the list and build the total (logic inside the arrow function).
  3. Within the arrow function, we first get the details of the fish belonging to the current iteration using the key (e.g. “fish1”) and store it in a fish variable.
  4. We then get the quantity of that fish from the order state and put that in a count variable, again using the key.
  5. The final variable we create checks if the fish still exists (i.e. it hasn’t been removed) and (using &&) that it’s available, storing the boolean outcome in a variable called isAvailable.
  6. We then use an if statement where, if ifAvailable is true, return the reduce method’s accumulator (the previous total in this iteration process) and add to it the quantity of the current fish iteration multiplied by its price.
  7. If the fish wasn’t available, the if statement wouldn’t run and instead we return the prevTotal accumulator. unchanged, ready to go through the next iteration.
  8. The final line is an optional parameter for the reduce method which represents its initial value. If you don’t set it, the initial value is set to the first element in the array. In our case, we want to start our calculation from zero.

All of the above results in us being able to render {total} within our JSX to display an up-to-date, reactive total order value.

Creating ‘Render Functions’

Moving on to displaying the individual fish details in the order, we start to write some JavaScript in our component’s returned JSX that starts to feel a little ‘messy’. Luckily, Wes has a little tip here!

If there’s a fair amount going on within the JSX but it doesn’t make sense to create a whole child component, consider ‘offloading’ that specific part to what he calls a ‘render function’. It’s essentially exactly the same code, just moved out of the returned JSX called from within it. Let me show you Wes’ example.

Say we want to render our list of fish that a customer has ordered, we might do it like this:

<ul>
  {orderIds.map((key) => (
    <li>
      <span>{this.props.fishes[key].name}</span>
      <span>{formatPrice(this.props.fishes[key].price)}</span>
    </li>
  ))}
</ul>

Considering this is in amongst all of our other JSX, you can see how it can become a bit much.

Instead, Wes suggests we put this in a function outside of our render and return statements, like this:

renderOrder = (key) => {
  return (
    <li>
      <span>{this.props.fishes[key].name}</span>
      <span>{formatPrice(this.props.fishes[key].price)}</span>
    </li>
  );
};

Our JSX then simply becomes:

<ul>
  {orderIds.map(this.renderOrder)}
</ul>

Video 18: Persisting our State with Firebase

In this video, things get really exciting! We learn how to setup a real-time database and mirror the state of our app so that the inventory data for each store persists beyond a single browser session.

For the real-time database, we use Google’s Firebase. It’s easy to get up and running with, has a nice UI and offers a free tier which is perfect for us testing things out.

As a side note, this will use a thing called WebSockets. It’s an API introduced by HTML5 that’s used to connect browsers and servers, allowing them to communicate with each other. The WebSockets API allows real-time communication, as oppose to the traditional polling that you might do with AJAX.

Without further ado, let’s get started!

Setup Firebase

We first need to go to Firebase and login with our Google account (assuming everyone has one!).

We should see our ‘Your Firebase projects’ screen like the screenshot below:

Click ‘Add project’ and give your project a name. In this case, perhaps “catch of the day” and add your name on the end to make it unique (everyone that’s followed this course will have use similar names!).

Click ‘Continue’, then choose whether or not you want to enable Google Analytics (no real need in this case). Finally, click ‘Create project’ and Firebase will start putting your new project together.

You should then be redirected to your project’s ‘Project Overview’ screen (below).

From here, click the ‘Database’ navigation item on the left-hand side.

From the screen below, click ‘Create database’.

Select ‘Start in test mode‘ and click ‘Next’.

Choose a server location that’s closest to you and click ‘Done’.

Once that’s created, click the ‘Rules’ tab, then the ‘Edit rules’ button. When the rules editor shows, change the values of both the .read and .write properties to true (see below).

Once you’ve made the edits, click the white ‘Publish’ button.

Once published, go back to the Project Overview screen and click the ‘Web’ option (third from left icon in white, next to the Android logo).

Give your app a name on the screen that’s displayed, then click ‘Register app’.

When your app is registered, Firebase should show you the JavaScript SDK configuration code for your project.

Keep this tab open and let’s go back to our React project.

Setup Firebase in App

We’re going to start by creating a file called base.js in the /src folder that initialises our connection to the Firebase database that we just created.

In that file, import the two packages that we need; Rebase and firebase:

  1. Rebase – a library that makes it easier to work with Firebase by binding the data in the Firebase database with a React component’s state.
  2. firebase – the official SDK that we’re loading via our package manager rather than including the <script> line Firebase provided.

We can import these like so:

import Rebase from 're-base';
import firebase from 'firebase';

We then need to create a function called firebaseApp that initialises the connection and is passed some of our authentication credentials. Copy apiKey, authDomain and databaseURL from the Firebase tab we left open and put inside the function.

const firebaseApp = firebase.initializeApp({
  apiKey: "##############################",
  authDomain: "###############.firebaseapp.com",
  databaseURL: "https://################.firebaseio.com"
})

Then we create a reference to the Rebase package and pass in the initialised app.

const base = Rebase.createClass(firebaseApp.database());

Finally, we use a named export for the firebaseApp function and a default export for base.

export { firebaseApp };
export default base;

Lifecycle Methods

Before we move on, a quick note on lifecycle methods.

Much like with vanilla JavaScript and jQuery where we can hook into specific points in the timeline of a page loading (e.g. document.ready), lifecycle methods in React provide us with the ability to hook into specific moments in the life of a component.

In this video, we’ll make use of both the componentDidMount and the componentWillUnmount lifecycle methods:

  • componentDidMount – invoked immediately after a component is mounted
  • componentWillUnmount – invoked immediately before a component is unmounted and destroyed

Mirror State to the Database

Now that we have our connection to Firebase initialised and a basic understanding of React’s lifecycle methods, it’s time to put it all together.

Go to your App.js file and import the base.js file like so:

import base from "../base";

Then we’ll setup the synchronisation between the app and the database by hooking into React’s componentDidMount lifecycle method like this:

componentDidMount() {
  const { params } = this.props.match;
  this.ref = base.syncState(${params.storeId}/fishes, {
    context: this,
    state: 'fishes'
  })
}

Taking this line by line:

  1. Using destructuring, we create a variable equal to the contents of the params property (which houses our store ID) that we can get from React Router.
  2. We then create a reference to the syncState method in order to setup two-way data binding between the app and Firebase. The reason we create a reference here is so that we can easily remove the two-way binding later.
  3. We then pass syncState two things: the endpoint (where to store and sync the app’s state in our database) and then a couple of options. context refers to the context of the component and state refers to the property in state that we want to sync.

That relatively short function has now enabled a real-time connection between the app and the database. It’s absolutely incredible to see this in action considering how easy (relatively speaking) it was to setup!

Finally, to avoid any memory leaks when going back to the store entrance page, we need to ‘close’ the two-way connection (or binding) between our app and our database. We do this with the componentWillUnmount lifecycle method which, as we know, will run just before the component (App in this case) is unmounted and destroyed.

componentWillUnmount() {
  base.removeBinding(this.ref);
}

Video 19: Persisting Order State with localstorage

Another way for us to persist our app’s data that doesn’t need to be in a database is to use localStorage. Local storage essentially lives in the user’s browser and data is stored as key:value tokens that can be retrieved across page reloads and browser sessions with no expiration date. It’s a bit like Cookies, but meant to be easier to work with (we’ll see!).

To see it in action in Chrome, open Dev Tools and go to the Application tab, then on the left-hand side go to Local Storage under the Storage tab.

Storing State in Local Storage

Working within our App component, we use a new lifecycle method called componentDidUpdate() that is invoked immediately after updating occurs. The React team really did a great job here using descriptive names in their framework. 😊

Within our lifecycle method we use setItem() to store our key:value pairing of our store ID and our order (the arguments are keyName and keyValue).

componentDidUpdate() {
  localStorage.setItem(
    this.props.match.params.storeId,
    JSON.stringify(this.state.order)
  );
}

Wes shows us that if we were to store this.state.order as the value, we’d see a string representation of that object ([object Obect]) instead of the object’s contents. This is where JSON.stringify() comes in as it converts the object into a string, like {"fish2":1,"fish1":1}.

So the function above makes sure that the order state is stored in local storage, but what if we refresh or return to the page later? The app doesn’t yet know it needs to load the data.

Loading Data from Local Storage

To load any data stored for our given store ID, we need to go to our existing componentDidMount() and add a conditional loading statement.

const localStorageRef = localStorage.getItem(params.storeId);
if (localStorageRef) {
  this.setState({ order: JSON.parse(localStorageRef) });
}

First, we use getItem() which takes keyName as its only argument. Bearing in mind we set the keyName to be storeID, that’s what we pass here.

We then use an if conditional to check if our getItem() method found anything and, if it did, set the state equal to the keyValue in local storage.

Remember that we used JSON.stringify() to convert our order object? Well, we need to convert it back! Luckily, it’s really simple; we just use JSON.parse() and pass it the ‘stringified’ object.

Wes was right – using localStorage does seem pretty damn easy!

Video 20: Bi-directional Data Flow and Live State Editing

The focus of this video is on implementing real-time inventory editing through the use of inputs.

In previous videos, we’ve covered handling form submissions such as the StorePicker and AddFishForm, but here we’re talking about live editing; you change the price of a fish and it changes on the menu and in the order panel automatically.

What actually happens behind the scenes when this is all setup is:

  1. User changes an input field.
  2. React intercepts that change and instead updates the state.
  3. Because the input’s value is set to the corresponding value stored in state, the field then updates automatically from the state.

There’s three main parts to it:

  1. Setting up your input element
  2. Handling the input’s onChange event
  3. Updating state via a method in the parent component

Setup Your Input

Here’s the example that we create from the video:

<input
  type="text"
  name="name"
  onChange={this.handleChange}
  value={this.props.fish.name}
/>

Beyond being a basic text input with it’s standard type attribute, there are three key parts to it:

  1. name – This makes it easier to reference later, without needing a React ref.
  2. onChange – All inputs need an onChange event and handler, otherwise React will make the input read-only.
  3. value – Finally, we set the value of the input by using props that, in turn, come from state.

Handle the onChange Event

So we have our input field. Let’s pretend someone has tried to make a change to the value in that field. We now need to intercept that change and make the change to state instead. We do this with the onChange event and create a corresponding method like below:

handleChange = (event) => {
  const updatedFish = {
    ...this.props.fish,
    [event.currentTarget.name]: event.currentTarget.value,
  };
  this.props.updateFish(this.props.index, updatedFish);
};

Here we’re taking a copy of the fish as it is stored in state, then updating the corresponding property with the edited value.

A cool thing that we’re using here is computed property names to handle which property to update. By putting the property’s key in square brackets and instead including a reference it essentially becomes dynamic. This works well here as we have various fields that a user can edit, so this computed property name will match whichever field is edited (and thus triggers the onChange event). Therefore, there’s no need to create separate functions to handle changes to the different fields.

Finally, we call a method to update the state (which doesn’t live in this component).

Create a Method for Updating State

We then need to create that method that we called from the child component in order to update the state.

updateFish = (key, updatedFish) => {
  const fishes = { ...this.state.fishes };
  fishes[key] = updatedFish;
  this.setState({ fishes });
};

This is a fairly straightforward method that takes the key of the fish to update and the fish object itself (which includes its name, price, etc). We take a copy of the current state, we update that copy with the new information, then we use setState to update the state.

Connecting the Dots

Each of the components we’re dealing with here need certain information, so make sure you pass things down accordingly. For example:

  • Pass the part of state that’s being updated down from the parent to the child so that it can take a copy and pass it back.
  • Pass down the method that will update state from the parent to the child so it can call it when the input changes.
  • Remember that this.updateFish in the parent component becomes this.props.updateFish in the children it’s passed to.

Video 21: Removing Items from State

This was one of the shorter videos in the course, but for good reason. Removing items from state is very similar to how we add them to state.

Create a Method for Removing an Item from State

In the same way we created our methods like addFish or updateFish, we setup a deleteFish method like this:

deleteFish = (key) => {
  const fishes = {...this.state.fishes}
  fishes[key] = null
  this.setState({fishes})
}

The only real difference here is that we are setting the specific fish to null. We set it to null (rather than use the delete keyword) because it’s a requirement for Firebase to update.

Calling the Method

We then setup the event handler on the delete/remove button which we can do inline, calling the deleteFish method accordingly and passing the key of the specific fish that was clicked.

<button onClick={() => this.props.deleteFish(this.props.index)}>Remove Fish</button>

Ta da! It’s as simple as that.

Video 22: Animating React Components

While this video is a relatively long one, a large amount of it is the legwork of writing CSS as Wes illustrates what’s going on ‘under the hood’.

Note: if you come across any issues with running the npm run styles:watch or npm run watch commands – you may have to install the stylus, autoprefixer-stylus and concurrently packages globally via npm (e.g. npm install stylus -g).

Right, let’s get started with animating React components!

Importing the Required Components

First, we need to import a couple of components from a package provided by the React community, react-transition-group.

import { TransitionGroup, CSSTransition } from "react-transition-group";

This package makes it easier implementing transitions based on components entering and exiting.

Setup the TransitionGroup Component

The first one we’ll setup is TransitionGroup.

This component manages a set of transition components (its children) and we supply it with a component that it will render and wrap its children in. In the case below, we’re simply creating a TransitionGroup parent container that will wrap the<li> elements created by our renderOrder method in a <ul> element.

<TransitionGroup component="ul">
  {orderIds.map(this.renderOrder)}
</TransitionGroup>

This component can make use of some other options that you can view here.

Setup the CSSTransition Component

The other imported component that we’re using here is called CSSTransition.

This is where we provide some more details about our transition, such as the class names we want to use and timing values.

<CSSTransition
  classNames="order"
  key={key}
  timeout={{ enter: 500, exit: 500 }}
>
  <li key={key}>
    Sorry {fish ? fish.name : "fish"} is no longer available
  </li>
</CSSTransition>

There are two ‘new’ properties we’re working with above:

  1. classNames – This is the class name prefix that the CSSTransition component will use for the different ‘lifecycle’ events. For example, if classNames is set to order, we will see classes such as order-enter-done and order-exit-active. More on this in a bit!
  2. timeout – This controls the timings of the lifecycle events and the values are in milliseconds.

Now, without any CSS animating the transitions, we can use Chrome Dev Tools to inspect the <li> elements and, when adding and removing them, we can see the different classes being added and removed along with them. These classes are using the classNames prefix we set and the timing values from timeout.

Alternative Method for Storing CSSTransition Options

That concludes this specific lesson on animating React components, but there’s a handy tip that Wes gives us for if we ever find ourselves repeating transition options over and over for different components.

Instead of explicitly passing the CSSTransition component its options there in the component itself, we can instead create an object that holds them. For example:

const transitionOptions = {
  classNames: "order",
  key,
  timeout: { enter: 500, exit: 500 },
};

This means we’ve got a re-usable object and only one place where we need to change the transition options if we ever want to tweak them.

To pass the object to the CSSTransition component, we use the object spread syntax like below:

<CSSTransition {...transitionOptions}>

Video 23: Component Validation with PropTypes

One way to make our app (and any app, really!) more resilient is to validate the type of data we are passing to our components. For example, if we are displaying a price using data provided by a prop we might explicitly state that we are expecting a number.

If you’ve heard of TypeScript, this ‘type checking’ is fundamental to it. Another option is Facebook’s Flow. In our React app, we’ll use a package called PropTypes instead that offers something similar.

In practice, the PropTypes package will let us set the type of data for each specific prop for a component and then display an error in the browser’s console if we pass the wrong type of data. It’s worth knowing that this is primarily a development tool – the errors won’t pull through in production.

Import the PropTypes Package

As usual, to get started we need to import the prop-types package.

import PropTypes from "prop-types";

This was originally part of the core React package, but was eventually split into its own thing. Perhaps because of the popularity of things like TypeScript and availability of other options out there, it becomes less necessary to include it in the core package.

Validating Props for Stateless Functional Components

To use the package in our simple stateless, functional components, we need to declare our ‘rules’ after the component has been created. We do it like so:

Header.propTypes = {
  tagline: PropTypes.string
}

There are three key parts to this that might change from component to component:

  1. The component’s name, e.g. Header
  2. The prop’s name, e.g. tagline
  3. The data type, e.g. string (find other types here)

Also note the initial lowercase propTypes for the component’s name followed by the uppercase PropTypes for the data type.

Validating Props for Class Components

For class components, we can do it exactly the same way as above. The alternative is to put the validation within the component itself, just after the initial declaration.

class Fish extends React.Component {
  static propTypes = {
    addToOrder: PropTypes.func
  };
  
  // the rest...

Other Validation Options

PropTypes also gives us some additional options for validating our props. For instance, we can add isRequired to make sure that a prop is set (as opposed to blank).

tagline: PropTypes.string.isRequired

We could also declare specifically the value we are expecting by using oneOf.

tagline: PropTypes.oneOf(['Tagline 1', 'Tagline 2'])

There are lots more here.

Video 24: Authentication

In this video, Wes shows us how to lock down our app so that only the ‘owner’ of the store can edit the inventory section. We’ll be establishing the owner via authentication; more specifically, using Firebase’s authentication options to setup Facebook, Twitter and GitHub as sign-in methods.

Note / Warning: You may find this video harder to follow than most – I did! After we get access to the different platform’s APIs and Firebase is set up, we move on to the implementation of the app’s logic for handling the authentication. If you are relatively new to JavaScript or haven’t dived too deeply into it yet, this part will be a challenge to follow. Wes flies through this pretty quickly without explaining every part in detail (which is understandable as React is the core focus of this course, not JavaScript). I’ve tried to explain each line of code in the relevant part towards the end of this section. Hope it helps!

Getting Started in Firebase

First, we need to go to Firebase and click the Authentication tab on the left under Develop.

We’ll then be presented with the Authentication screen (below), where we need to click the blue ‘Set up sign-in method’ button.

We should then be presented with the screen below which lists all of the sign-in methods we have available to us with Firebase.

Set Up Facebook Authentication

To setup Facebook authentication, click Facebook in the list of providers within Firebase. As you’ll see, it needs two pieces of information; an ‘App ID’ and an ‘App secret’ (a bit like a password).

To get these, go to developers.facebook.com and click the ‘My Apps’ link in the top right of the screen.

If you’re already logged in, you’ll be presented with the screen below where you’ll need to click ‘Add a New App’.

Name it “Catch of the Day” and enter your email address, then click the ‘Create App ID’ button.

On the next screen you see (below), click Settings, then Basic in the menu on the left-hand side navigation.

From this screen, copy the App ID (the first field in the main area of the screen below) and then go back to Firebase. Click the ‘Enable’ toggle to enable this sign-in method and then paste the App ID in the first field.

Then go back to Facebook and click ‘Show’ on the App Secret field which is just next to the App ID field. After entering your Facebook password to confirm, copy the App Secret and then go back to Firebase and paste it in the second field.

The next step is to add our OAuth redirect URI to our Facebook app, so copy the URL Firebase has provided and go back to Facebook.

We’ll want to click Dashboard in the menu on the left, then scroll down to the ‘Add a product’ section.

Find ‘Facebook Login’ (which should be the first grid item) and click the ‘Set Up’ button.

You’ll automatically be taken to the Quickstart wizard which we’ll want to ignore in this case. Instead, click Settings in the navigation on the left of the screen.

On this screen, we’ll want to toggle the ‘Embedded Browser OAuth Login’ to “Yes” and paste the Firebase URI that we copied into the ‘Valid OAuth Redirect URIs’ field.

Once those two things are done, click Save Changes.

Go back to Firebase and click Save. This sign-in method should now be enabled!

Set Up Twitter Authentication

Let’s do the same with Twitter!

This time, we’ll need an API key and an API secret.

Go to developer.twitter.com and, if you’re signed in, you should see the screen below.

Click ‘Create an app’ button.

If you don’t currently have a developer account, you’ll see a message like the below.

Go through the application process which is fairly straightforward, but you’ll have to wait for your application to be approved (you may just need to confirm your email address).

After clicking the ‘Create an app’ button, you’ll be shown the screen below.

Complete the following fields:

  • App name – e.g. “Catch of the Day” and your twitter handle (has to be unique)
  • Application description – e.g. “Catch of the Day app – React for Beginners course”
  • Website URL – e.g. http://wp.robcallaghan.co.uk/
  • Enable Sign in with Twitter – tick this
  • Callback URLs – paste in the URL from Firebase
  • Tell us how this app will be used – e.g. “I will be using this app to enable users to login to my app using their Twitter account. The app I am building is part of a video course teaching React.”

If all is well, we should be able to click the blue ‘Create’ button at the bottom. Once you accept their policies, you should see the screen below.

Go to the ‘Keys and tokens’ tab along the top and copy across the information (API key and API secret key) to the relevant fields in Firebase.

Set Up GitHub Authentication

Finally, let’s setup GitHub which needs a Client ID and a Client secret.

To get this information, we’ll want to go to github.com/settings/applictions/new. Once you’re here, give the app a name, set the Homepage URL (if you have a website or blog, just use that) and then paste the Authorization callback URL from Firebase into the final field.

Click the green ‘Register application’ button and, if successful, you’ll be taken to the app’s details (below).

Copy and paste the Client ID and Client Secret across to the relevant fields in Firebase, then click Save.

Implementing the Authentication Logic

First, Wes creates the Login.js component that displays the different login buttons and handles their onClick methods. I won’t paste the code for the component here as it’s mostly just a regular component, however the key part in this file is that when a user clicks one of the login buttons, we pass the name of that service through to our authenticate method that we’ll create next.

Handling Authentication

As stated, when a user clicks a login button we need to let them, well, login! We’ll do this by displaying a pop-up depending on which button they clicked and we’re able to choose which pop-up to show because we’ve provided the name of the service when the click happens. This means we just need one authentication method, not multiple.

authenticate = (provider) => {
  const authProvider = new firebase.auth[`${provider}AuthProvider`]();
  firebaseApp.auth().signInWithPopup(authProvider).then(this.authHandler);
};

So, what’s going on here?

We start by defining our method name (authenticate) and use ES6 arrow function syntax to accept a provider (e.g. "Github" or "Facebook").

We then create a variable (authProvider) within which we store a new instance of the authentication provider object for the respective sign-in provider. We dynamically determine the provider by using ES6 template literal syntax within bracket notation (as opposed to dot notation which we’re used to using with objects) and use the provider that was passed as an argument to this method. This article explains dot versus bracket notation nicely. If it wasn’t dynamic, this line might look like this instead:

const authProvider = new firebase.auth.FacebookAuthProvider();

We then need to show the pop-up window and handle the result.

We do this by referencing the firebaseApp variable that we setup in video 18 to initialise the connection and then call signInWithPopup and pass it our authProvider variable that we just created.

After this, we use a JS method called then() to return a Promise. then can take two parameters, onFulfilled and onRejected, which are functions called when it essentially succeeds or fails. They’re both optional parameters and in this case we only use one parameter (the first one), which we pass this.authHandler. This is a method of our Inventory component that will handle updating our app’s state with ownership information. We’ll create this method next.

Using Authenticated User Data

When a user successfully signs in using their chosen method, we want to assign ownership of the current store to that user.

For our authHandler method, we use an async (asynchronous) function and pass it our authData which contains all of the data that came back from our user sign-in / authentication (e.g. the user’s email address).

authHandler = async (authData) => {
  const store = await base.fetch(this.props.storeId, { context: this });
  if (!store.owner) {
    await base.post(`${this.props.storeId}/owner`, {
      data: authData.user.uid,
    });
  }
  this.setState({
    uid: authData.user.uid,
    owner: store.owner || authData.user.uid,
  });
};

Inside our method, we start by creating a reference / variable (store) that fetches the current store from the Firebase database (using the await keyword enabled by our async function and our Rebase package’s fetch method).

We then check that if there is no owner we’ll set the owner as the current logged in user. We do this by posting owner data to the database (as opposed to fetching) using Rebase’s post method.

The final thing we want to do in this method is to set the local state of the Inventory component to reflect the current user.

Logging Out a User

To logout a user, we create an async function called logout that calls Firebase’s signOut() method and clear the user ID (uid) from state.

logout = async () => {
  await firebase.auth().signOut();
  this.setState({ uid: null });
};

For future projects, we can all the logout method any way we want however Wes provides a nice and simple way of rendering the login/logout logic and interface which also checks whether the logged-in user is the owner of that particular store.

const logout = <button onClick={this.logout}>Log Out!</button>;

if (!this.state.uid) {
  return <Login authenticate={this.authenticate} />;
}

if (this.state.uid !== this.state.owner) {
  return (
    <div>
      <p>Sorry you are not the owner!</p>
      {logout}
    </div>
  );
}

Persisting Login Status

Refreshing page, automatically log user back in

Now that all of the logic is in place, we want to make sure that when we refresh the page the app knows whether or not we were logged in.

To do that, we need to listen for the componentDidMount lifecycle method and use Firebase’s onAuthStateChanged() method to check if we’re logged in and get the logged-in user for us to pass on to our authHandler method which will take care of all of the checks on store ownership, etc.

componentDidMount() {
  firebase.auth().onAuthStateChanged((user) => {
    if (user) {
      this.authHandler({ user });
    }
  });
}

…and that’s it! Authentication logic complete.

Now, we just need to make the app a bit more secure.

Locking Down Firebase

To lock down our app, we need to go to Firebase and find our Database, then click in to the Rules tab.

Wes provides the complete set of rules (in JavaScript) to paste into Firebase here.

{
  "rules": {
    // won't let people delete an existing room
    ".write": "!data.exists()",
    ".read": true,
    "$room": {
      // only the store owner can edit the data
      ".write":
        "auth != null && (!data.exists() || data.child('owner').val() === auth.uid)",
      ".read": true
    }
  }
}

Video 25: Building React for Production

TBC

One reply on “Course Review: ‘React for Beginners’ by Wes Bos (2020)”

Leave a Reply

Your email address will not be published. Required fields are marked *