Building Shopping Cart With Reactjs and Redux

Building Shopping Cart With Reactjs and Redux Part 1

In this tutorial we will build a shopping cart demo using Reactjs the javascript framework and Redux store manager, i will also using the react router to navigate between the cart and the products pages.

 

 

Requirements

  • Knowledge with javascript
  • Knowledge with Reacjs and redux

 

In this tutorial we will create two pages, the first page contains a list of products and the second page is the shopping cart. To facilitate things i am not going to do any server side logic so i suppose that the products and the cart will be fetched from Redux store only.

 

Let’s start by creating a new React app with this command:

npx create-react-app shopping-cart

Next cd into shopping-cart/ directory and install those dependencies:

npm install react-router-dom redux react-redux

I will be using the react-router to navigate between pages and redux and react-redux for the store management.

Now run this command to start our app:

npm start

This will open and watch the app in a browser window using a random port for example http://localhost:3000

I will be using bootstrap so include the cdn in file public/index.html like this:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="theme-color" content="#000000" />
    
    <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
    
    <title>React App</title>
    <link href="//maxcdn.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css" rel="stylesheet" id="bootstrap-css">
    <link href="./style.css" rel="stylesheet" />
  </head>
  <body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root"></div>
  </body>
</html>

public/style.css

.card-product .img-wrap {
    border-radius: 3px 3px 0 0;
    overflow: hidden;
    position: relative;
    height: 220px;
    text-align: center;
}
.card-product .img-wrap img {
    max-height: 100%;
    max-width: 100%;
    object-fit: cover;
}
.card-product .info-wrap {
    overflow: hidden;
    padding: 15px;
    border-top: 1px solid #eee;
}
.card-product .bottom-wrap {
    padding: 15px;
    border-top: 1px solid #eee;
}

 

Preparing Components & Store

We need to add the required components and store for the app. The components and store in Reactjs located in the src/ directory so open the src/ directory and create those directories and files:

src/

    — components/

        — cart/

                   — Cart.js

                   — Item.js

        — Navbar.js

        — Product.js

        — ProductList.js

    — store/

          — actions/

                         — cartActions.js

          — reducers/

                          — cartReducer.js

                          — productReducer.js

                          — rootReducer.js

    — App.js

    — Index.js

 

As you see in the directory structure above i have created two directories the first for the components and the second for the store. For the components we have components that display a list of products those are ProductList.js and Product.js. The cart components will be in the cart/ directory which is Cart.js and Item.js

For the store i created two directories one for the actions and the second for the reducers, as with any other React app i separated the reducers to product reducer and this in productReducer.js and a cart reducer in cartReducer.js and combined them with the root reducer located in rootReducer.js

 

Next let’s add the components code with dummy content and applying the router, in the next section i will go on by implementing the Redux.

src/components/Product.js

import React, { Component } from 'react';

class Product extends Component
{
    render() {

        return (
            <div className="col-md-3">
                <figure className="card card-product">
                    <div className="img-wrap">
                        <img className="img-responsive" src="http://placehold.it/300x200" />
                    </div>
                    <figcaption className="info-wrap">
                        <h4 className="title">Product title</h4>
                        <p className="desc">Product description</p>
                    </figcaption>
                    <div className="bottom-wrap">
                        <a href="#" className="btn btn-sm btn-primary float-right">Add to cart</a>
                        <div className="price-wrap h5">
                            <span className="price-new">$1280</span>
                        </div>
                    </div>
                </figure>
            </div>
        )
    }
}

export default Product;



src/components/ProductList.js

import React, { Component } from 'react';
import Product from './Product';


class ProductList extends Component
{
    render() {
        return (
            <div className="container">
                <h2>Product List</h2>
                <br/>
                <div className="row">
                    <Product />
                    <Product />
                    <Product />
                </div>
            </div>
        )
    }
}


export default ProductList;

src/components/cart/Item.js

import React, { Component } from 'react';


class Item extends Component {

  render() {

      return (

          <div className="row">
              <div className="col-xs-2"><img className="img-responsive" src="http://placehold.it/300x200" />
              </div>
              <div className="col-xs-4">
                  <h4 className="product-name"><strong>Product title</strong></h4>
              </div>
              <div className="col-xs-6">
                  <div className="col-xs-3 text-right">
                      <h6><strong>330 <span className="text-muted">x</span></strong></h6>
                  </div>
                  <form>
                      <div className="col-xs-4">
                          <input type="number" className="form-control input-sm" />
                      </div>

                      <div className="col-xs-2">
                          <button type="submit" className="btn btn-info">Update</button>
                      </div>

                      <div className="col-xs-2">
                          <button type="button" className="btn btn-link btn-xs">
                              <span className="glyphicon glyphicon-trash"> </span>
                          </button>
                      </div>
                  </form>
              </div>
          </div>
      )
  }
}

export default Item;

src/components/cart/Cart.js

import React, { Component } from 'react';
import Item from './Item';

class Cart extends Component
{
    render() {

        return (
                <div className="container">
                    <div className="row">
                        <div className="col-md-12 col-xs-12">
                            <div className="panel panel-info">
                                <div className="panel-heading">
                                    <div className="panel-title">
                                        <div className="row">
                                            <div className="col-xs-6">
                                                <h5><span className="glyphicon glyphicon-shopping-cart"></span> My Shopping Cart</h5>
                                            </div>

                                        </div>
                                    </div>
                                </div>

                                <div className="panel-body">
                                       <Item />
                                       <Item />
                                </div>
                                <div className="panel-footer">
                                   <div className="row text-center">
                                      <div className="col-xs-11">
                                        <h4 className="text-right">Total <strong>$5400.8</strong></h4>
                                      </div>

                                   </div>
                                 </div>

                            </div>
                        </div>
                    </div>
                </div>
        )
    }
}

export default Cart;

src/components/Navbar.js

import React, { Component } from 'react';
import { NavLink } from 'react-router-dom';

class Navbar extends Component {

    render() {

        return (

            <nav className="navbar navbar-default">
                <div className="container-fluid">
                    <div className="navbar-header">
                        <NavLink className="navbar-brand" to="/">Shopping cart</NavLink>
                    </div>

                    <div className="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
                        <ul className="nav navbar-nav navbar-right">
                            <li><NavLink to="/my-cart"><i className="glyphicon glyphicon-shopping-cart"></i> My Cart</NavLink></li>
                        </ul>
                    </div>
                </div>
            </nav>
        )
    }
}

export default Navbar;



src/App.js

import React from 'react';
import { BrowserRouter, Switch, Route } from 'react-router-dom'
import ProductList from "./components/ProductList";
import Cart from "./components/cart/Cart";
import Navbar from "./components/Navbar";

function App(props) {

  return (
      <BrowserRouter>
        <div className="App">
          <Navbar />
          <Switch>
             <Route exact path="/" component={ProductList} />
             <Route path="/my-cart" component={Cart} />
          </Switch>
        </div>
      </BrowserRouter>
  );
}

export default App;

The above code is a blueprint of what we will go to implement with redux, if you run the app you will see the products in the home page and you can navigate to the cart page by clicking on the top right icon, now we need to update those items to be coming from redux, we will implement this in the next section.

 

Applying Redux Store

By using redux we are able to store the products and cart items in the store and we can access them in the products and cart pages respectively for display. Also we need to enable the user to add products to cart, remove products from cart, update product quantity, we will accomplish this using the action creators and reducers.

 

We need to add the Redux store to our app so Update src/Index.js as follows:

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import * as serviceWorker from './serviceWorker';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import rootReducer from './store/reducers/rootReducer'

const store = createStore(rootReducer);

ReactDOM.render(<Provider store={store}><App /></Provider>, document.getElementById('root'));

serviceWorker.unregister();

As shown i have imported the createStore and Provider dependencies, then i create a new store instance using createStore() passing in the rootReducer, finally i updated the render function to include the <Provider /> component as the root component this makes the entire app to be aware of the store and reducer.

If you run the app you will see an error telling that the root reducer is missing don’t worry we will fix that below.

 

Displaying products

The first step is to display the products so open and modify src/store/reducers/productReducer.js

const initialState = {
  products: [
      {id: 1, title: 'Apple iPhone 8 Plus Gold 256GB 4G', description: 'Meld style and practicality with the Apple iPhone 8 Plus smartphone', price: '649.54', image: 'https://i.ebayimg.com/images/g/2tQAAOSwnhldR6hD/s-l640.jpg', amount: 5},
      {id: 2, title: 'Apple MacBook Pro Core i5 2.5GHz 13"', description: 'This MacBook Pro has been professionally restored to working order by an approved vendor', price: '339.97', image: 'https://i.ebayimg.com/images/g/AbEAAOSw2FJc3cCF/s-l1600.jpg', amount: 3},
      {id: 3, title: 'Canon EOS M50 Mirrorless Camera Body', description: '2160p UHD Video Recording, Built-in Flash, Body only, Auto Focus, AE/FE Lock, Tripod Thread', price: '450.00', image: 'https://i.ebayimg.com/images/g/PV8AAOSwX4FdRIac/s-l1600.jpg', amount: 4},
      {id: 4, title: 'VIZIO D-Series D24F-F1 24" Full HD Smart TV ', description: 'VIZIO D-Series D24F-F1 24" Full HD LED Smart TV. Condition is Manufacturer refurbished', price: '104.99', image: 'https://i.ebayimg.com/images/g/Pr0AAOSwd9ZdMgTG/s-l1600.jpg', amount: 2}
  ]
};

const productReducer = (state = initialState, action) => {

    return state;
};

export default productReducer;

In the above i have added an initial state of of four products in the store, in a real world project those products will come from server side Api. The product reducer is just a function that returns the state.

src/store/reducers/cartReducer.js

const initialState = {
  cart: []
};

const cartReducer = (state = initialState, action) => {
   return state;
};

export default cartReducer;

src/store/reducers/rootReducer.js

import productReducer from './productReducer';
import cartReducer from './cartReducer';
import { combineReducers } from 'redux';

const rootReducer = combineReducers({
    product: productReducer,
    cart: cartReducer
});

export default rootReducer;

The root reducer combine the product reducer and cart reducer you can of it as doing a merge of the two reducers each with a distinct key so for example to access the products in any component you can use state.product.products.



Now modify the product list and product components as follows:

src/components/ProductList.js

import React, { Component } from 'react';
import Product from './Product';
import { connect } from 'react-redux';


class ProductList extends Component
{
    
    render() {
        return (
            <div className="container">
                <h2>Product List</h2>
                <br/>
                <div className="row">

                    {
                        this.props.products.map(product => <Product product={product} key={product.id} /> )
                    }

                </div>
            </div>
        )
    }
}

const mapStateToProps = (state) => {

    return {
        products: state.product.products
    }
};


export default connect(mapStateToProps)(ProductList)

src/components/Product.js

import React, { Component } from 'react';

class Product extends Component
{
    render() {

        const { product } = this.props;

        return (
            <div className="col-md-3">
                <figure className="card card-product">
                    <div className="img-wrap">
                        <img className="img-responsive" src={product.image} />
                    </div>
                    <figcaption className="info-wrap">
                        <h4 className="title">{product.title}</h4>
                        <p className="desc">{product.description}</p>
                    </figcaption>
                    <div className="bottom-wrap">
                        <a href="#" className="btn btn-sm btn-primary float-right">Add to cart</a>
                        <div className="price-wrap h5">
                            <span className="price-new">${product.price}</span>
                        </div>
                    </div>
                </figure>
            </div>
        )
    }
}

export default Product;

To access the state in the components we have to create a function that map the state into props like the function i have added in ProductList.js called mapStateToProps().

mapStateToProps() function takes the state as a parameter and it should return an object with the props we need to access, in this case we need to access the products.

Then we need to pass that function to the component, to do this we use the connect() function from redux. The connect() function return a higher order component which means it injects additional props to the component ProductList. Now you can access the products from inside the component using this.props.products

Finally i looped through the products using javascript map() function and in each iteration i return a <Product /> component passing in the product as a prop like this:

this.props.products.map(product => <Product product={product} key={product.id} />

Now refresh the browser you will see the real products coming from the store.

 

Continue to part 2: Handling Cart

5 1 vote
Article Rating
Share this: