This content originally appeared on SitePoint and was authored by Deven Rathore
In this tutorial, we’ll look at how to build a very simple ecommerce web application with React. It won’t be the next Shopify, but hopefully it will serve as a fun way to demonstrate how well suited React is to building dynamic and engaging user interfaces.
The app will demonstrate a basic cart management system, as well as a simple method of handling user authentication. We’ll make use of React Context as an alternative to state management frameworks such as Redux or MobX, and we’ll create a fake back end using the json-server package.
Below is a screenshot of what we’ll be building:
The code for this application is available on GitHub.
Prerequisites
This tutorial assumes you have a basic knowledge of JavaScript and React. If you are new to React, you might like to check out our beginner’s guide.
To build the application, you’ll need a recent version of Node installed on your PC. If this isn’t the case, then head over to the Node home page and download the correct binaries for your system. Alternatively, you might consider using a version manager to install Node. We have a tutorial on using a version manager here.
Node comes bundled with npm, a package manager for JavaScript, with which we’re going to install some of the libraries we’ll be using. You can learn more about using npm here.
You can check that both are installed correctly by issuing the following commands from the command line:
node -v
> 12.18.4
npm -v
> 6.14.8
With that done, let’s start off by creating a new React project with the Create React App tool. You can either install this globally, or use npx
, like so:
npx create-react-app e-commerce
When this has finished, change into the newly created directory:
cd e-commerce
In this application, we’ll use React Router to handle the routing. To install this module, run:
npm install react-router-dom
We’ll also need json-server and json-server-auth to create our fake back end to handle authentication:
npm install json-server json-server-auth
We’ll need axios for making Ajax requests to our fake back end.
npm install axios
And we’ll need jwt-decode so that we can parse the JWT that our back end will respond with:
npm install jwt-decode
Finally, we’ll use the Bulma CSS framework to style this application. To install this, run the following command:
npm install bulma
Getting Started
First, we need to add the stylesheet to our application. To achieve this, we’ll add an import statement to include this file in the index.js
file in the src
folder. This will apply the style sheet across all the components in the application:
import "bulma/css/bulma.css";
Context Setup
As previously mentioned, we’ll be using React Context throughout our app. This is a relatively new addition to React and provides a way to pass data through the component tree without having to pass props down manually at every level.
If you’d like a refresher on using context in a React application, check out our tutorial “How to Replace Redux with React Hooks and the Context API”.
In complex applications where the need for context is usually necessary, there can be multiple contexts, with each having its own data and methods relating to the set of components that requires the data and methods. For example, there can be a ProductContext
for handling the components which use product-related data, and another ProfileContext
for handling data related to authentication and user data. However, for the sake of keeping things as simple as possible, we’ll use just one context instance.
In order to create the context, we create a Context.js
file and a withContext.js
files in our app’s src
directory:
cd src
touch Context.js withContext.js
Then add the following to Context.js
:
import React from "react";
const Context = React.createContext({});
export default Context;
This creates the context and initializes the context data to an empty object. Next, we need to create a component wrapper, which we’ll use to wrap components that use the context data and methods:
// src/withContext.js
import React from "react";
import Context from "./Context";
const withContext = WrappedComponent => {
const WithHOC = props => {
return (
<Context.Consumer>
{context => <WrappedComponent {...props} context={context} />}
</Context.Consumer>
);
};
return WithHOC;
};
export default withContext;
This might look a little complicated, but essentially all it does is make a higher-order component, which appends our context to a wrapped component’s props.
Breaking it down a little, we can see that the withContext
function takes a React component as its parameter. It then returns a function that takes the component’s props as a parameter. Within the returned function, we’re wrapping the component in our context, then assigning it the context as a prop: context={context}
. The {...props}
bit ensures that the component retains any props that were passed to it in the first place.
All of this means that we can follow this pattern throughout our app:
import React from "react";
import withContext from "../withContext";
const Cart = props => {
// We can now access Context as props.context
};
export default withContext(Cart);
Scaffolding Out the App
Now, let’s create a skeleton version of the components we’ll need for our app’s basic navigation to function properly. These are AddProducts
, Cart
, Login
and ProductList
, and we’re going to place them in a components
directory inside of the src
directory:
mkdir components
cd components
touch AddProduct.js Cart.js Login.js ProductList.js
In AddProduct.js
add:
import React from "react";
export default function AddProduct() {
return <>AddProduct</>
}
In Cart.js
add:
import React from "react";
export default function Cart() {
return <>Cart</>
}
In Login.js
add:
import React from "react";
export default function Login() {
return <>Login</>
}
And finally, in ProductList.js
add:
import React from "react";
export default function ProductList() {
return <>ProductList</>
}
Next, we need to set up up the App.js
file. Here, we’ll be handling the application’s navigation as well as defining its data and methods to manage it.
First, let’s set up up the navigation. Change App.js
as follows:
import React, { Component } from "react";
import { Switch, Route, Link, BrowserRouter as Router } from "react-router-dom";
import AddProduct from './components/AddProduct';
import Cart from './components/Cart';
import Login from './components/Login';
import ProductList from './components/ProductList';
import Context from "./Context";
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
user: null,
cart: {},
products: []
};
this.routerRef = React.createRef();
}
render() {
return (
<Context.Provider
value={{
...this.state,
removeFromCart: this.removeFromCart,
addToCart: this.addToCart,
login: this.login,
addProduct: this.addProduct,
clearCart: this.clearCart,
checkout: this.checkout
}}
>
<Router ref={this.routerRef}>
<div className="App">
<nav
className="navbar container"
role="navigation"
aria-label="main navigation"
>
<div className="navbar-brand">
<b className="navbar-item is-size-4 ">ecommerce</b>
<label
role="button"
class="navbar-burger burger"
aria-label="menu"
aria-expanded="false"
data-target="navbarBasicExample"
onClick={e => {
e.preventDefault();
this.setState({ showMenu: !this.state.showMenu });
}}
>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
</label>
</div>
<div className={`navbar-menu ${
this.state.showMenu ? "is-active" : ""
}`}>
<Link to="/products" className="navbar-item">
Products
</Link>
{this.state.user && this.state.user.accessLevel < 1 && (
<Link to="/add-product" className="navbar-item">
Add Product
</Link>
)}
<Link to="/cart" className="navbar-item">
Cart
<span
className="tag is-primary"
style={{ marginLeft: "5px" }}
>
{ Object.keys(this.state.cart).length }
</span>
</Link>
{!this.state.user ? (
<Link to="/login" className="navbar-item">
Login
</Link>
) : (
<Link to="/" onClick={this.logout} className="navbar-item">
Logout
</Link>
)}
</div>
</nav>
<Switch>
<Route exact path="/" component={ProductList} />
<Route exact path="/login" component={Login} />
<Route exact path="/cart" component={Cart} />
<Route exact path="/add-product" component={AddProduct} />
<Route exact path="/products" component={ProductList} />
</Switch>
</div>
</Router>
</Context.Provider>
);
}
}
Our App
component will be responsible for initializing the application data and will also define methods to manipulate this data. First, we define the context data and methods using the Context.Provider
component. The data and methods are passed as a property, value
, on the Provider
component to replace the object given on the context creation. (Note that the value can be of any data type.) We pass the state value and some methods, which we’ll define soon.
Next, we build our application navigation. To achieve this, we need to wrap our app with a Router
component, which can either be BrowserRouter
(like in our case) or HashRouter
. Next, we define our application’s routes using the Switch
and Route
components. We also create the app’s navigation menu, with each link using the Link
component provided in the React Router module. We also add a reference, routerRef
, to the Router
component to enable us to access the router from within the App
component.
To test this out, head to the project root (for example, /files/jim/Desktop/e-commerce
) and start the Create React App dev server using npm start
. Once it has booted, your default browser should open and you should see the skeleton of our application. Be sure to click around and make sure all of the navigation works.
Spinning up a Fake Back End
In the next step, we’ll set up a fake back end to store our products and handle user authentication. As mentioned, for this we’ll use json-server to create a fake REST API and json-server-auth to add a simple JWT-based authentication flow to our app.
The way json-server works is that it reads in a JSON file from the file system and uses that to create an in-memory database with the corresponding endpoints to interact with it. Let’s create the JSON file now. In the route of your project, create a new backend
folder and in that folder create a new db.json
file:
mkdir backend
cd backend
touch db.json
Open up db.json
and add the following content:
{
"users": [
{
"email": "regular@example.com",
"password": "$2a$10$2myKMolZJoH.q.cyXClQXufY1Mc7ETKdSaQQCC6Fgtbe0DCXRBELG",
"id": 1
},
{
"email": "admin@example.com",
"password": "$2a$10$w8qB40MdYkMs3dgGGf0Pu.xxVOOzWdZ5/Nrkleo3Gqc88PF/OQhOG",
"id": 2
}
],
"products": [
{
"id": "hdmdu0t80yjkfqselfc",
"name": "shoes",
"stock": 10,
"price": 399.99,
"shortDesc": "Nulla facilisi. Curabitur at lacus ac velit ornare lobortis.",
"description": "Cras sagittis. Praesent nec nisl a purus blandit viverra. Ut leo. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Fusce a quam."
},
{
"id": "3dc7fiyzlfmkfqseqam",
"name": "bags",
"stock": 20,
"price": 299.99,
"shortDesc": "Nulla facilisi. Curabitur at lacus ac velit ornare lobortis.",
"description": "Cras sagittis. Praesent nec nisl a purus blandit viverra. Ut leo. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Fusce a quam."
},
{
"id": "aoe8wvdxvrkfqsew67",
"name": "shirts",
"stock": 15,
"price": 149.99,
"shortDesc": "Nulla facilisi. Curabitur at lacus ac velit ornare lobortis.",
"description": "Cras sagittis. Praesent nec nisl a purus blandit viverra. Ut leo. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Fusce a quam."
},
{
"id": "bmfrurdkswtkfqsf15j",
"name": "shorts",
"stock": 5,
"price": 109.99,
"shortDesc": "Nulla facilisi. Curabitur at lacus ac velit ornare lobortis.",
"description": "Cras sagittis. Praesent nec nisl a purus blandit viverra. Ut leo. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Fusce a quam."
}
]
}
We’re creating two resources here — users
and products
. Looking at the users
resource, you’ll notice that each user has an ID, an email address and a password. The password appears as a jumble of letters and numbers, as it’s encrypted using bcryptjs. It’s important that you don’t store passwords in plain text anywhere in your application.
That said, the plain text version of each password is simply “password” — without the quotes.
Now start up the server by issuing the following command from the root of the project:
./node_modules/.bin/json-server-auth ./backend/db.json --port 3001
This will start json-server on http://localhost:3001
. Thanks to the json-server-auth middleware, the users
resource will also give us a /login
endpoint that we can use to simulate logging in to the app.
Let’s try it out using https://hoppscotch.io. Open that link in a new window, then change the method to POST
and the URL to http://localhost:3001/login
. Next, make sure the Raw input switch is set to on and enter the following as the Raw Request Body:
{
"email": "regular@example.com",
"password": "password"
}
Click Send and you should receive a response (further down the page) that looks like this:
{
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6InJlZ3VsYXJAZXhhbXBsZS5jb20iLCJpYXQiOjE2MDE1Mzk3NzEsImV4cCI6MTYwMTU0MzM3MSwic3ViIjoiMSJ9.RAFUYXxG2Z8W8zv5-4OHun8CmCKqi7IYqYAc4R7STBM"
}
That is a JSON Web Token, which is valid for an hour. In a normal app with a proper back end, you’d save this in the client, then send it to the server whenever you requested a protected resource. The server would validate the token it received and if everything checked out, it would respond with the data you requested.
This point is worth repeating. You need to validate any request for a protected resource on your server. This is because the code that runs in the client can potentially be reverse engineered and tampered with.
Here’s a link to the finished request on Hoppscotch. You just need to press Send.
If you’d like to find out more about using JSON Web Tokens with Node.js, please consult our tutorial.
Continue reading How to Create an Ecommerce Site with React on SitePoint.
This content originally appeared on SitePoint and was authored by Deven Rathore
Deven Rathore | Sciencx (2020-10-14T04:00:23+00:00) How to Create an Ecommerce Site with React. Retrieved from https://www.scien.cx/2020/10/14/how-to-create-an-ecommerce-site-with-react/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.