Create a Shopping Item App using React Js and Xstate ( state management )
In this post, we will create a shopping item app using React js and Xstate. Generally, we use redux for state management but in this application, I am going to show you state management with the help of Xstate.
|
Shopping Item App using React Js and Xstate |
Dependency:
npm install xstate @xstate/react
Folder Structure:
Sample Code:
A) index.js
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
ReactDOM.render(<App />, document.getElementById("root"));
B) App.js
import React from 'react';
import Product from './Product/Product';
import './style.css';
import {ShoppingContext, ShoppingMachine} from './State/index'
import { useMachine } from '@xstate/react';
export default function App() {
const [currentStates, sendToMachine] = useMachine(ShoppingMachine);
return (
<ShoppingContext.Provider value={[currentStates, sendToMachine]}>
<div id="product">
<Product />
</div>
</ShoppingContext.Provider>
);
}
C) State/index.js
import { createContext } from 'react';
import { assign, createMachine } from 'xstate';
export const ShoppingContext = createContext();
window.id = 1;
const addItemsToList = async (context, event) => {
let items = {
id: window.id++,
productName: event.productName,
price: event.price,
quantity: event.quantity,
};
return items;
};
export const ShoppingMachine = createMachine({
id: 'ShoppingCart',
initial: 'INIT',
context: {
shoppingItems: [],
totalAmount: 0,
totalItemCount: 0,
},
states: {
INIT: {},
ADD_ITEMS: {
states: {
LOADING: {
invoke: {
id: 'addItemToList',
src: addItemsToList,
onDone: {
target: 'SUCCESS',
actions: assign({
shoppingItems: (context, event) => {
let item = event.data;
return context.shoppingItems.concat(item);
},
totalAmount: (context, event) => {
let price = Number(event.data.price);
let quantity = Number(event.data.quantity);
let totalPrice = context.totalAmount + price * quantity;
return totalPrice;
},
totalItemCount: (context, event) => {
return context.totalItemCount + 1;
},
}),
},
onError: {
target: 'FAIL',
actions: assign({ error: (context, event) => event.data }),
},
},
},
SUCCESS: {},
FAIL: {},
},
},
},
on: {
ADD_SHOPPING_ITEM: {
target: 'ADD_ITEMS.LOADING',
},
},
});
D) Header/Header.js
import React from 'react';
function Header() {
return <div>Shopping Item App</div>;
}
export default Header;
E) Product/AddProduct.js
import React, { useState, useContext } from 'react';
import { ShoppingContext } from '../State/index';
function AddProduct(props) {
const [productName, setProductName] = useState('');
const [price, setPrice] = useState(0);
const [quantity, setQuantity] = useState(0);
const [currentStates, sendToMachine] = useContext(ShoppingContext);
const AddItem = () => {
sendToMachine('ADD_SHOPPING_ITEM', { productName, price, quantity });
};
const onChangeProductName = (e) => {
setProductName(e.target.value);
};
const onChangePrice = (e) => {
setPrice(e.target.value);
};
const onChangeQuantity = (e) => {
setQuantity(e.target.value);
};
return (
<div id="addproduct">
<label>Product Name</label>
<input type="text" onChange={onChangeProductName} /> <br />
<label>Price</label> <br />
<input type="text" onChange={onChangePrice} />
<label>Quantity</label>
<input type="text" onChange={onChangeQuantity} />
<button id="button" onClick={AddItem}> Add Item </button>
</div>
);
}
export default AddProduct;
F) Product/Product.js
import React from 'react';
import Header from '../Header/Header';
import AddProduct from './AddProduct';
import ShowProductList from './ShowProductList';
import ShowTotalProduct from './ShowTotalProduct';
function Product() {
return (
<div>
<div id="header">
<Header />
</div>
<div id="left">
<div id="addProduct">
<AddProduct />
</div>
<div id="totalProduct">
<ShowTotalProduct />
</div>
</div>
<div id="right">
<div>
<ShowProductList />
</div>
</div>
</div>
);
}
export default Product;
G) Product/ShowProductList.js
import React, { useContext } from 'react';
import { ShoppingContext } from '../State/index';
function ShowProductList(props) {
const [currentStates, sendToMachine] = useContext(ShoppingContext);
return (
<div>
<div id="tableHeader">
<div id="column">Sr#</div>
<div id="column">ProductName</div>
<div id="column">Price</div>
<div id="column">Quantity</div>
</div>
<div>
{currentStates.matches('ADD_ITEMS.SUCCESS') &&
currentStates.context.shoppingItems.map((item) => {
return (
<div id="row" key={item.id}>
<div id="column">{item.id}</div>
<div id="column">{item.productName}</div>
<div id="column">{item.price}</div>
<div id="column">{item.quantity}</div>
</div>
);
})}
</div>
</div>
);
}
export default ShowProductList;
H) Product/ShowTotalProduct.js
import React, { useContext } from 'react';
import { ShoppingContext } from '../State/index';
function ShowTotalProduct(props) {
const [currentStates, sendToMachine] = useContext(ShoppingContext);
return (
<div id="totalItemPrice">
<label>Total Item : {currentStates.context.totalItemCount} </label> <br />
<label>Total Price : {currentStates.context.totalAmount} </label>
</div>
);
}
export default ShowTotalProduct;
I) style.css
#header {
color: white;
font-weight: blold;
margin-bottom: 20px;
margin-left: 20px;
}
#product {
border: 1px solid black;
width: 575px;
background-color: gray;
margin-top: 50px;
height: 300px;
}
#right {
border: 1px solid black;
margin-left: 270px;
background-color: white;
border-radius: 10px;
height: 250px;
width: 275px;
}
#totalProduct {
border: 1px solid black;
width: 220px;
background-color: white;
border-radius: 10px;
margin-left: 20px;
height: 85px;
}
#left {
width: 250px;
float: left;
height: 250px;
}
#addProduct {
border: 1px solid black;
color: black;
width: 220px;
height: 150px;
background-color: white;
border-radius: 10px;
margin-left: 20px;
margin-bottom: 10px;
}
#tableHeader {
background: #000;
color: #fff;
display: table-row;
font-weight: bold;
}
#row {
display: table-row;
}
#row:nth-child(odd) {
background-color: gray;
}
#row:nth-child(even) {
background-color: white;
}
#column {
display: table-cell;
padding: 6px 6px 6px 6px;
width: 120px;
}
#addproduct {
margin: 6px;
}
#totalItemPrice {
margin: 10px;
}
button {
margin-top: 3px;
color: white;
background-color: blue;
}
J) package.json
{
"name": "react",
"version": "0.0.0",
"private": true,
"dependencies": {
"@xstate/fsm": "^1.0.0",
"@xstate/react": "^1.6.3",
"react": "17.0.2",
"react-dom": "17.0.2",
"xstate": "^4.28.1"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
},
"devDependencies": {
"react-scripts": "latest"
}
}
Happy Learning!! Happy Coding!!
Good post
ReplyDelete