Mideo | React Firebase App

This is a simple React application that makes use of OMDb API to allow the user to search and display Movie/TV Show titles, we also make use of Firebase Auth to authenticate the users and the Database to store the user’s favorite titles. This is not a tutorial on how to build this application but rather a set of notes about the most important or interesting aspects, the rest is self-explanatory with basic React components knowledge.

Feel free to clone the repo and/or watch the video demo on my channel.

This is what the final directory structure looks like:

Generally speaking, we should have a clear separation of stateful (container) and stateless (presentational) components, however, since this is a small application, we are not going to take that consideration. Let’s start with our main (renders all other components) App component:

Our /src/components/App.js  component is very straightforward, as soon as the component mounts, we attach an observer that looks for authentication state changes (this will determine the value of state.authed  and whether or not state.currentUser  has a value), then the render method wraps everything in a <BrowserRouter>  which makes use of the HTML5 history API, inside this render method, all we do is call the other components, the heart of our app lies in the Home component, however, let’s go one by one starting from the top:

The handleGoogle  method is the one doing most of the job here, first of all, we create a new instance of the GoogleAuthProvider and add the scopes that we require (I like using the API Explorer to find out the scopes), we then choose to use the popup method, however, we could’ve used the redirect method. This is going to return a promise that, should it be successful, will include a results object, from which we store the user object in a variable, this user variable is going to be passed to the saveUserGoogle  function, let’s take a look at this function in  src/helpers/helpers.js :

The purpose of this function is to save a copy of the user object to our Database reference (ref), notice how we don’t make a distinction as to whether or not the user already exists, this will guarantee that we always copy the most current information every time the user logs in, also, notice for simplicity we are are taking for granted that the .set will always be successful, however, here are the different ways of handling this.

Going back to our Header component, if signInWithPopup is unsuccessful we will for simplicity just log the errors using the catch() method.

Let’s jump over to our Home component, we have a number of states to hold some of the data that we will use throughout the life cycle of the application, state.Title  and state.Year  will get updated every time the form fields receive data, this is what we have the updateTitle and updateYear methods for, that’s very simple, let’s take a look at our queryOMDB function (instructions of this API can be found here) and the submitForm method:

So when the user submits the form, we simply store the select field value in a variable called type, then if the Title field is empty, we will update our state.formError and that will cause a re-rendering that will display this message, otherwise if the Title field contains a string, we pass all these values to queryOMDB as parameters.

In our case, we’re using axios for our requests, and because it is promise based, we can make use of then() after the request is completed. Notice for simplicity we are not handling any errors that may have arrived with the promise, but rather we assume that the response was successful, the type of responses we can expect from OMDb API are in the form of:

So knowing each query returns a page with 10 titles, we can use the totalResults property to build our navigation system, which is what we start doing after we receive the results from the axios promise. Then because results.data.Search is an array, we can use the map method to operate over each element, each element is going to be one title in our table, so here we can create an instance of the Title component per element in the array, so each Title component will represent a <tr> in our table, pretty cool right ?

Let’s jump over to Title component and take a look at it, it will be simple to render these because we are passing the information that we are going to need ( eachTitle and currentUser) at the time of component instantiation:

An interesting part is the expandTitle method that we call every time the <tr> element is clicked, notice we make use of  searchDetails function, which executes an axios request to the OMDb API, this time, passing the imdbID as a parameter, which returns a single object with all the title details as properties, like this one.

The other interesting part is the Favorite component that we render in this Title’s component render method:

As soon as the component mounts we check to see if there is a currentUser logged in, if so, we use the title’s imdbID and the user’s uid to create a Database reference and find out whether or not this title is one of the user’s favorites, this will allow us to know if we should render a grey or a yellow star icon.

Notice we make use of stopPropagation method in the clickOnStar function, this is because the parent <tr> in which this component resides also has an onClick function ( expandTitle) that would be called every time we click the star if we didn’t use stopPropagation.

The two helper functions ( removeFav, addFav) that we call based on whether the title is already a favorite or not should be self-explanatory after looking at the firebase Database API reference.

The last component that we render in our App.js render method is the Dashboard, for which we use a custom route called PrivateRoute located in our /src/helpers/helpers.js file:

This route takes an object argument, in which we pass a component, the authed state and a rest parameter so we may pass any other number of parameters in the future. The main pointer to take from this, is that if the authed state passed happens to be true, then we will render the passed component and make the rest parameter available as props, instead, if authed is false, we will use Redirect to send the user to the "/" route (which renders the Home component). There is a really good example of authentication based redirects in the rr4 documentation.

The nice thing about the firebase database is that is real-time, so we can attach a listener to a database reference path, similarly to how we did to listen for authentication state changes, this time we attach a listener to look for changes on the users/uid/favorites path, if a value change occurs, we instantiate a favs array and push a Title component instance per element in the array (the number of titles the user has in the favorites).

When the component unmounts, we simply remove the listener.