Build Amazon Clone with React

Build Amazon Clone with React, Context API, and Firebase


Amazon is a vast Internet-based enterprise that sells books, music, movies, housewares, electronics, toys, and many other goods. In this article, we will explore the react hooks and context API as we build the amazon clone using the react context API for state management within our app.

If you want to learn about React Native animation, then use this brilliant guide.

Pre-requisite

  • Basic understanding of JavaScript ES6
  • Basic understanding of HTML and CSS
  • Have Nodejs installed on your machine

Technologies

  • React
  • Firebase
  • Font awesome (icons)

Project overview

Our clone will allow users to register, login, add products to the shopping cart, remove product from the shopping cart. Our clone will permit only an authenticated user to access the home page.

Here is a live demo of our clone – https://amazonna.netlify.app/

Project setup

Let’s get started as we create a new project using the create-react-app so go to a directory where you will store this project and type the following in the terminal

create-react-app amazon-clone

The above command uses the create-react-app CLI tool to generate a react boilerplate project for us so that we don’t have to configure any tooling.

For the command above to work, the create-react-app CLI tool must be installed globally using the command below.

npm install -g create-react-app

Spin up the server with the following:

cd amazon-clone
npm start

We will delete some files that are not necessary for this project. These files include (App.css, App.test.js, logo.svg and registerServiceWorker.js)

Next, make the following change to the index.js file

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
ReactDOM.render(<App/>, document.getElementById('root'));

Setting up Firebase

In this project, we will use firebase for user authentication.

Firebase is provides developers with servers, APIs and datastore, all written so generically that developers can modify it to suit most needs. It is user friendly
In this article we will use the firestore to store our data.

Head to fiarebase.google.com and sign up. It is free for small projects.

Next you will be directed to your firebase projects page like the following:

Click on the Add project button.
step 1 :
Add the name of your project (amazon-clone)
step 2 :
check enable Google Analytics for this project and click continue
step 3 :
select default account for firebase.

After the firebase project is successfully created, click on the web icon and follow the prompt to register your app.

Next Install Firebase CLI

 npm install -g firebase-tools

and continue to console.

Next, click on the web icon and select the config option as follow:

Connect our project to firebase

Install firebase in the project

npm install firebase

Create a firebase.js file in the src folder with the following:

import firebase from "firebase";

 const firebaseConfig = {
    // paste your firebase config object here
 }
const firebaseApp = firebase.initializeApp(firebaseConfig)
const auth = firebase.auth();
export { auth };

React Hooks

Since we will be writing functional based components, I’ll briefly discuss what react hooks are as we’ll be using some of them in this article.

According to the React Docs:

“Hooks are a new addition in React 16.8. They let you use state and other React features without writing a class.”

THE useState HOOK
The useState hook allows us to use state in our functional components similar this.state in class based component. A useState hook takes the initial value of our state as the only argument, and it returns an array of two elements. The first element is our state variable and the second element is a function in which we can use the update the value of the state variable.

Let’s take a look at the following example:

import React, {useState} from "react";

function Counter(){
   const [counter, setCounter] = useState(0);
}

Here, counter is our state variable and its initial value is 0 while setCounter is a function which we can use to update the value of count.

THE useContext HOOK
This hook basically allows us to consume the value of a context. It Accepts a context object (the value returned from createContext and returns the current context value for that context. For more information check https://reactjs.org/docs/hooks-reference.html

The Context API

According to the React documentation:

“Context provides a way to pass data through the component tree without having to pass props down manually at every level.”

In simpler terms, Context API provides a way for you to make particular data available to all components throughout the component tree no matter how deeply nested that component may be.

Now that we have an understanding of what the context API is, let’s create one for our project.

In the component folder, create a stateProvider folder containing StateProvider.js with the following:

// setup data layer
// we need this to track the basket
import React, { createContext, useContext, useReducer } from "react";
//This is the data layer
export const StateContext = createContext()
//Build a provider
export const StateProvider = ({reducer, initialState, children}) => (
    <StateContext.Provider value={useReducer(reducer, initialState)}>
        {children}
    </StateContext.Provider>
)
//This is how we use it inside of a component
export const useStateValue = () => useContext(StateContext)

The StateProvider will be used to wrap our entire app so that every component can access the StateContext (ie the data layer).

Create a reducer.js file in the src folder with the following:

export const initialState = {
    basket: [],
    user: null
}

export const getBasketTotal = (basket) => basket?.reduce((amount, item) => item.price + amount, 0);

function reducer(state, action) {
    switch(action.type) {
        case "SET_USER": 
            return {
                ...state,
                user: action.user
            }
        case "ADD_TO_BASKET":
            return {
                ...state, 
                basket: [...state.basket, action.item]
            };
        case 'REMOVE_FROM_BASKET':
            let newBasket  = [...state.basket];
            const index = state.basket.findIndex((basketItem) => basketItem.id === action.id);
            if(index >= 0) {
                //item exist in the basket, remove it
                newBasket.splice(index, 1) 
            } else {
                console.warn (
                    `Can't remove product{id: ${action.id}} as it is not in the basket`
                );
            }
            return{
                ...state,
                basket: newBasket
            };
        default:
            return state
    }
}
export default reducer;

Update App.js as follows:

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import { StateProvider } from '../src/components/stateProvider/StateProvider'
import reducer, { initialState } from './reducer';
ReactDOM.render(
  <React.StrictMode>
    <StateProvider initialState={initialState} reducer={reducer}>
      <App />
    </StateProvider>
  </React.StrictMode>,
  document.getElementById('root')
);

The StateProvider is used to wrap our entire app so that every component can access the StateContext (ie the data layer).

Components

Our amazon clone will consist of the following components:

  • Header
  • Login
  • Product
  • Subtotal
  • Checkout
  • Home
  • CheckoutProduct In the src folder, create a folder called components with the following folders header, login,product,subtotal,checkout,home and checkoutProduct. We will install react-router-dom a dependency for routing in react apps, @material-ui/core ,@material-ui/icons for icons. npm install react-router-dom
    npm install @material-ui/core
    npm install @material-ui/icons

Header Component

The header comprises of the amazon logo, search field, cart icon and the number of items in the user’s basket. If a user is logged in, he will have these Hello user@gmail.com and Sign Out text on his header component. Otherwise, he will have Hello and Sign In

In the header folder, create Header.js file with the following:

import React from 'react'
import {Link, useHistory} from "react-router-dom"
import './header.css'
import SearchIcon from "@material-ui/icons/Search"
import ShoppingBasketIcon from "@material-ui/icons/ShoppingBasket"
import {useStateValue } from "../stateProvider/StateProvider"
import { auth } from 'firebase'
function Header() {
    const [{basket, user}] = useStateValue();
    const history = useHistory();
    const login = () => {
        if (user) {
            auth().signOut();
            history.push("/login")
        }
    }
    return (
        <nav className="header">
            {/* logo on the left -> img */}
            <Link to="/">
                <img className="header__logo" src="http://pngimg.com/uploads/amazon/amazon_PNG11.png" alt="amazon logo"/>
            </Link>
            {/* search box */}
            <div className="header__search">
                <input type="text" className="header__searchInput"/>
                <SearchIcon className="header__searchIcon"/>
            </div>
            {/* 3 links */}
            <div className="header__nav">
                <Link to={!user && "/login"} className="header__link">
                    <div onClick={login} className="header__option">
                        <span className="header__optionLineOne">Hello {user?.email}</span>
                        <span className="header__optionLineTwo">{user ? "Sign Out" : "Sign In"}</span>
                    </div>
                </Link>
            </div>
            <div className="header__nav">
                <Link to="/order" className="header__link">
                    <div className="header__option">
                        <span className="header__optionLineOne">Returns</span>
                        <span className="header__optionLineTwo">& Orders</span>
                    </div>
                </Link>
            </div>
            <div className="header__nav">
                <Link to="/login" className="header__link">
                    <div className="header__option">
                        <span className="header__optionLineOne">Your</span>
                        <span className="header__optionLineTwo">Prime</span>
                    </div>
                </Link>
            </div>
            <Link to="/checkout" className="header__link">
                <div className="header__optionBasket">
                    {/* shopping basket icon */}
                    <ShoppingBasketIcon/>
                    {/* number of items in the basket */}
                    {/* we use {basket?.length} to render the length of the basket when basket property of the state reaches the header component. without this, the dom will render the basket.length even when it has not reach the header component therby resulting to an error  */}
                    <span className="header__optionLineTwo header__basketCount">{basket?.length}</span>
                </div>
            </Link>
            {/* basket icon with number*/}
        </nav>
    )
}
export default Header

To access the basket and user object, we destructured them from the state in the context API as follows:

import {useStateValue } from "../stateProvider/StateProvider"
const [{basket, user}] = useStateValue();

In the header folder, create Header.css file with the following:

.header{
    background-color: #131921;
    display: flex;
    align-items: center;
    position: sticky;
    top: 0;
    z-index: 100;
}
.header__logo{
    width: 100px;
    margin: 0 20px;
    margin-top: 18px;
    object-fit: contain;

}
.header__optionBasket{
    display: flex;
    align-items: center;
}
.header__basketCount{
    margin: 0px 10px;
}
.header__nav{
    display: flex;
    justify-content: space-evenly;
}
.header__option{
    display: flex;
    flex-direction: column;
    margin: 0px 10px;
}
.header__optionLineOne{
    font-size: 10px;
}
.header__optionLineTwo{
    font-size: 13px;
    font-weight: 800;
}
.header__link{
    color: #ffffff;
    text-decoration: none;
}
.header__search{
    display: flex;
    flex: 1;
}
.header__searchInput{
    padding: 10px;
    height: 12px;
    border: none;
    width: 100%;
}
.header__searchIcon{
    background-color: #cd9042;
    padding: 5px;
    height: 22px !important;
}

Home Component

This component imports products data from products and pass it to the product component as props. In the home folder, create Home.js file with the following:

import React from 'react'
import "./Home.css"
import Product from "../product/Product.js"
import { products,bannerImg } from '../../products'
function Home() {
    return (
        <div className="home">
            <img className="home__image" src={bannerImg} alt=""/>

            {/*product id, title, price, rating */}
            <div className="home__row">
                {products.map(item => {
                    return (
                        <Product
                            id={item.id}
                            title={item.title}
                            image={item.image}
                            price={item.price}
                            rating={item.rating}
                        />
                    )
                })}
            </div>
        </div>
    )
}
export default Home

In the home folder, create Home.css file with the following:

.home {
    max-width: 1500px;
    margin: 0px auto;
}
.home__row {
    display: flex;
    z-index: 1;
    margin: 0px 5px;
    flex-wrap: wrap;
    margin: 0px auto;
    justify-content: center;
}
.home__image{
    width: 100%;
    object-fit: contain;
    mask-image: linear-gradient(to bottom, rgba(0,0, 0, 0), rgba(0, 0,0, 1));
    margin-bottom: -150px;

}

products.js

This is the source of all the product data for our project.

In the src folder create products.js

export const products = [
    {
        id: 1,
        title: "Oculus Quest All-in-one VR Gaming Headset – 64GB",
        image: "https://images-na.ssl-images-amazon.com/images/I/31pEe2taIPL._AC_US327_FMwebp_QL65_.jpg",
        price: 11.96,
        rating: 4
    },
    {
        id: 2,
        title: "Nintendo Switch with Neon Blue and Neon Red Joy‑Con - HAC-001(-01)",
        image: "https://images-na.ssl-images-amazon.com/images/I/41DQoLIfsRL._AC_US327_FMwebp_QL65_.jpg",
        price: 15.96,
        rating: 3
    },
    {
        id: 3,
        title: "Xbox game",
        image: "https://images-na.ssl-images-amazon.com/images/G/01/amazonglobal/images/email/asins/DURM-2B5ECC8E3DA42415._V531815325_.jpg",
        price: 23.96,
        rating: 5
    },
    {
        id: 4,
        title: "The LeanStartup2: How constant innovative creators",
        image: "https://images-na.ssl-images-amazon.com/images/I/51T-sMqSMiL._SX329_BO1,204,203,200_.jpg",
        price: 9.96,
        rating: 4
    },
    {
        id: 5,
        title: "Play station game pad",
        image: "https://images-na.ssl-images-amazon.com/images/G/01/amazonglobal/images/email/asins/DURM-2B638E86650FFF18._V531815327_.jpg",
        price: 19.96,
        rating: 5
    }
]
export const bannerImg = "https://images-eu.ssl-images-amazon.com/images/G/02/digital/video/merch2016/Hero/Covid19/Generic/GWBleedingHero_ENG_COVIDUPDATE__XSite_1500x600_PV_en-GB._CB428684220_.jpg"

Product component

This component receives the product’s information as props, and displays it on the browser.
In the product folder, create Product.js file with the following:

import React from 'react'
import "./Product.css"
import { useStateValue } from "../stateProvider/StateProvider"
function Product({id, title, price, rating, image}) {
    const [{}, dispatch] = useStateValue();
    const addToBasket = () => {
        //Add item to basket...
        dispatch({
            type: "ADD_TO_BASKET",
            item: {
                id,
                title,
                image,
                price,
                rating
            }
        })
    };
    return (
        <div className="product">
            <div className="product__info">
                <p>{title}</p>
                <p className="product__price">
                    <small>$</small>
                    <strong>{price}</strong>
                </p>
                <div className="product__rating">
                    {Array(rating).fill().map((index) => (
                        <i key={index} class="fa fa-star"></i>
                    ))}
                </div>
            </div>
            <img src={image} alt=""/>
            <button onClick={addToBasket}>Add to basket</button>
        </div>
    )
}
export default Product

Add Products to Basket

In order to add product to the basket, the context API provides us with the dispatch method which dispatches actions to the reducer. and it is used as follows:

const [{}, dispatch] = useStateValue();
    const addToBasket = () => {
        //Add item to basket...
        dispatch({
            type: "ADD_TO_BASKET",
            item: {
                id,
                title,
                image,
                price,
                rating
            }
        })
    }; 

Whenever the dispatch method is invoked, the reducer checks the type property of the action and updates the state accordingly.

Let’s take a look at what the reducer does with this dispatched object.

...
function reducer(state, action) {
    switch(action.type) {
        ...
        case "ADD_TO_BASKET":
            return {
                ...state, 
                basket: [...state.basket, action.item]
            };
        ...
        default:
            return state
    }
}
...

Since the type of the object(action) which we dispatched corresponds with the case "``ADD_TO_BASKET``", the reducer updates the state with the new object that is added to the basket.

In the product folder, create Product.css file with the following:

.product {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: flex-end;
    width: 25%;
    max-height: 400px;
    min-width: 100px;
    background-color: white;
    margin: 10px;
    padding: 20px;
    z-index: 1;
}
.product__info {
    height: 100px;
    margin-bottom: 15px;
    align-self: flex-start;
}
.product__price {
    margin-top: 5px;
}
.product__rating{
    display: flex;
}
.product > img {
    width: 100%;
    max-height: 200px;
    object-fit: contain;
    margin-bottom: 15px;
}
.fa{
    color: #f0c14b;
}
.product > button {
    background-color: #f0c14b;
    border: 1px solid;
    border-color: #a88734 #9c7e31 #846a29;
}

CheckoutProduct Component

This component receives the product’s information from the checkout component as props, and displays it on the browser. It also allows the user to remove products from the basket.
In checkoutProduct folder, create CheckoutProduct.js file with the following:

import React from 'react'
import "./checkoutProduct.css"
import {useStateValue} from "../stateProvider/StateProvider"
function CheckoutProduct({id, image, title, price, rating}) {
    const [{}, dispatch] = useStateValue();
    const removeFromBasket = () => {
        // remove from basket
        dispatch({
            type: "REMOVE_FROM_BASKET",
            id
        })
    }
    return (
        <div className="checkoutProduct">
            <img src={image} alt=""/>
            <div className="checkoutProduct__info">
                <p className="checkoutProduct__title">{title}</p>
                <p className="checkoutProduct__price">
                    <small>$</small>
                    <strong>{price}</strong>
                </p>
                <div className="checkoutProduct__rating">
                    {Array(rating).fill().map((index) => (
                        <p key={index}>star</p>
                    ))}
                </div>
                <button onClick={removeFromBasket}>Remove from basket</button>
            </div>
        </div>
    )
}
export default CheckoutProduct

In checkoutProduct folder, create CheckoutProduct.css file with the following:

.checkoutProduct{
    display: flex;
    margin: 20px 0px;
}
.checkoutProduct__image {
    object-fit: contain;
    width: 100px;
    height: 180px;
}
.checkoutProduct__rating {
    display: flex;
}
.checkoutProduct__info {
    padding-left: 20px;
}
.checkoutProduct__title {
    font-size: 17px;
    font-weight: 800;
}
.checkoutProduct__info > button {
    background: #f0c14b;
    border: 1px solid;
    margin-top: 10px;
    border-color: #a88734 #9c7e31 #846a29;
    color: #111;
}

Remove Products from Basket

In order to remove products from the basket, we use the dispatch method to dispatch an action with type REMOVE_FROM_BASKET to the reducer as follows:

const removeFromBasket = () => {
      // remove from basket
      dispatch({
          type: "REMOVE_FROM_BASKET",
          id
      })
  }

The reducer checks for the case that matches the type property of the action and updates the state accordingly.

...
case 'REMOVE_FROM_BASKET':
    // Logic for removing item from basket
    //clone the basket
    let newBasket  = [...state.basket];
    const index = state.basket.findIndex((basketItem) => basketItem.id === action.id);
    if(index >= 0) {
        //item exist in the basket, remove it
        newBasket.splice(index, 1) 
    } else {
        console.warn (
            `Can't remove product{id: ${action.id}} as it is not in the basket`
        );
    }
    return{
        ...state,
        basket: newBasket
    };
...

SubTotal Component

This component displays the total amount of products added to the basket.

Context api

we will install react-currency-format to format the prices of products in dollars.

import React from 'react'
import CurrencyFormat from 'react-currency-format';
import {useStateValue} from "../stateProvider/StateProvider"
import "./Subtotal.css"
import { getBasketTotal } from '../../reducer';
function Subtotal() {
    const [{basket}, dispatch] = useStateValue();
    return (
        <div className="subtotal">
            {/* price */}
            <CurrencyFormat 
                renderText = {(value) => (
                    <>
                        <p>
                ( Subtotal {basket.length} items ) : <strong>{`${value}`}</strong>
                        </p>
                        <small className="subtotal__gift">
                            <input type="checkbox"/> This order contains
                        </small>
                    </>
                )}
                value={getBasketTotal(basket)} 
                displayType={'text'} 
                thousandSeparator={true} 
                prefix={'$'} 
            />

            <button>Proceed to checkout</button>
        </div>
    )
}
export default Subtotal

In the subTotal component, create SubTotal.css with the following:

.subtotal {
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    width: 300px;
    height: 100px;
    padding: 20px;
    background-color: #f3f3f3;
    border-radius: 3px;
    border: 1px solid #dddddd;
}
.subtotal > button {
    background: #f0c14b;
    border: 1px solid;
    margin-top: 10px;
    border-color: #a88734 #9c7e31 #846a29;
    color: #111;
}

Checkout Component

This component displays the list of products currently in the basket and the SubTotal component. It retrieves all the product data from the basket and passes it down to the CheckoutProduct component as props.

import React from 'react'
import { useStateValue } from "../stateProvider/StateProvider"
import "./Checkout.css"
import CheckoutProduct from "../checkoutProduct/CheckoutProduct"
import Subtotal from "../subtotal/Subtotal"
function Checkout() {
    const [{ basket }] = useStateValue()
    return (
        <div className="checkout">
            <div className="checkout__left">
                <img className="checkout__ad" src="https://images-na.ssl-images-amazon.com/images/G/01/AmazonExports/Fuji/2020/May/Hero/Fuji_TallHero_45M_v2_1x._CB432458380_.jpg" alt="ad"
                />
                {basket?.length === 0 ? (
                    <div>
                        <h2>Your shopping basket is empty</h2>
                        <p>
                            You have no items in your basket. To buy one or add item to basket click the add to basket button
                        </p>
                    </div>
                ) : (
                    <div>
                        <h2 className="checkout__title">Your shopping basket</h2>
                        {basket.map(item => {
                            console.log(item)
                        return (
                            <CheckoutProduct
                                id={item.id}
                                title={item.title}
                                image={item.image}
                                price={item.price}
                                rating={item.rating}
                            />
                        )
                        })}
                    </div>
                )}
            </div>
            {basket?.length > 0 && 
                <div className="checkout__right">
                    <Subtotal/>
                </div>
            }


        </div>
    )
}
export default Checkout

In the checkout folder, create Checkout.css with the following:

.checkout {
    display: flex;
    padding: 20px;
    background-color: white;
    height: max-content;
}
.checkout__title {
    margin-right: 10px;
    padding: 10px;
    border-bottom: 1px solid lightgray;
}
.checkout__ad {
    width: 100%;
    height: 100px;
    margin-bottom: 10px;
}

Login Component

This component uses the firebase auth module to sign up new users and also verify the sign up users each time they sign in.
In login folder create Login.js component with the following:

import React, {useState} from 'react'
import "./Login.css"
import {Link, useHistory} from "react-router-dom"
import {auth} from "../../firebase"
function Login() {
    const history = useHistory()
    const [email, setEmail] = useState("");
    const [password, setPassword] = useState("");
    const signIn = (event) => {
        event.preventDefault()
        auth.signInWithEmailAndPassword(email, password)
            .then( auth => {
                //redirect to home page
                history.push("/")
            })
            .catch(err => {
                alert(err.message)
            })
    }
    const register = (event) => {
        event.preventDefault()
        auth.createUserWithEmailAndPassword(email, password)
            .then(auth => {
                //create a user, login and redirect to homepage
                history.push("/")
            })
            .catch(err => {
                alert(err.message)
            })
    }
    return (
        <div className="login">
            <Link to="/">
                <img src="https://upload.wikimedia.org/wikipedia/commons/thumb/a/a9/Amazon_logo.svg/1024px-Amazon_logo.svg.png" alt="" className="login__logo"/>
            </Link>
            <div className="login__container">
                <h1>Sign in</h1>
                <form>
                    <h5>Email:</h5>
                    <input value={email} onChange={event => setEmail(event.target.value)} type="email"/>
                    <h5>Password:</h5>
                    <input value={password} onChange={event => setPassword(event.target.value)} type="password"/>
                    <button type="submit" onClick={signIn}  className="login__signInBtn">sign in</button>
                    <p>
                        by signing in you agree to amazon condition of use and sale.Please see our privacy notice, our cookies notice and our interest based ad notice. 
                    </p>
                    <button onClick={register} className="login__registerBtn">create your amazon account</button>
                </form>
            </div>
        </div>
    )
}
export default Login

In login folder create Login.css component with the following:

.login {
    background-color: white;
    height: 100vh;
    display: flex;
    flex-direction: column;
    align-items: center;
}

.login__container {
    width: 300px;
    margin: 0px auto;
    display: flex;
    flex-direction: column;
    padding: 20px;
    border: 1px solid lightgray;
}
.login__container > h1 {
    font-weight: 500;
    margin-bottom: 20px;
}
.login__container > form > h5 {
    margin-bottom: 5px;
}
.login__container > form > input {
    height: 30px;
    width: 98%;
    background-color: white;
    margin-bottom: 10px;
}
.login__container > form > p {
    margin-top: 15px;
    font-size: 12px;
}
.login__logo {
    width: 100px;
    object-fit: contain;
    margin: 20px 0px;
}
.login__signInBtn {
    width: 100%;
    background: #f0c14b;
    border: 1px solid;
    padding: 10px 0px;
    margin-top: 10px;
    border-color: #a88734 #9c7e31 #846a29;
    color: #111;
}
.login__registerBtn {
    width: 100%;
    background: lightgrey;
    border: 1px solid;
    padding: 10px 0px;
    margin-top: 10px;
    border-color: lightgrey;
    color: #111;
}

App component

When ever a user logs in or signs up, an action should be dispatched to the reducer to update the user property of the state with the authenticated user’s data. Furthermore, whenever a user logs out, an action should be dispatched to the reducer to set the user property of the state to a value of null.

Finally we need to update app.js with the as follows:

import React, { useEffect } from 'react';
import './App.css';
import Home from "./components/home/Home"
import {
  BrowserRouter as Router,
  Switch,
  Route,
} from "react-router-dom";
import Header from "./components/header/Header"
import Checkout from './components/checkout/Checkout';
import Login from './components/login/Login';
import {useStateValue} from "./components/stateProvider/StateProvider"
import {auth} from "./firebase"
function App() {
  const [{user}, dispatch] = useStateValue();
  useEffect(() => {
    const unsubscribe = auth.onAuthStateChanged(authUser => {
      if(authUser) {
        //The user is logged in
        dispatch({
          type: "SET_USER",
          user: authUser
        })
      } else {
        //The user is logged out
        dispatch({
          type: "SET_USER",
          user: null
        })
      }
    })
    return () => {
      // Any clean up operation goes in here
      unsubscribe();
    }
  }, [])
  return (
    <Router>
      <div className="App">
        <Switch>
          <Route path="/login">
            <Login/>
          </Route>
          <Route path="/checkout">
            <Header/>
            <Checkout/>
          </Route>
          <Route path="/">
            <Header/>
            <Home/>
          </Route>
        </Switch>
      </div>
    </Router>
  );
}
export default App;

Now we can checkout the features we have implemented so far in our amazon clone

npm start

Conclusion

That’s it! We have succeeded in building our Amazon clone using the Context API for state management and firebase for user authentication.
In the process, we have learned:

  • What the Context API is and the problem it solves;
  • When to use the Context API;
  • Creating Context and consuming it functional components.
  • What React Hook is
  • How to setup firebase authentication.

You can find the complete project for this article on Github. Happy coding.


Share on social media

//