In ProductScreen.js
- import
useState
- add
qty
, history
, addToCartHandler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
import React, { useState, useEffect } from "react";
const ProductScreen = ({ history, match }) => {
const [qty, setQty] = useState(1)
const addToCartHandler = () => {
history.push(`/cart/${match.params.id}?qty=${qty}`);
};
return (
<>
{product.countInStock > 0 && (
<ListGroup.Item>
<Row>
<Col>Qty</Col>
<Col>
<Form.Control
as="select"
value={qty}
onChange={(e) => setQty(e.target.value)}
>
{[...Array(product.countInStock).keys()].map((x) => (
<option key={x + 1} value={x + 1}>
{x + 1}
</option>
))}
</Form.Control>
</Col>
</Row>
</ListGroup.Item>
)}
<ListGroup.Item>
<Button
onClick={addToCartHandler}
className="btn-block"
type="button"
disabled={product.countInStock === 0}
>
Add To Cart
</Button>
</ListGroup.Item>
</ListGroup>
)}
</>
);
}
|
2. Cart Reducer & Add-to-Cart Action
Create frontend/src/constants/cartConstants.js
1
2
|
export const CART_ADD_ITEM = "CART_ADD_ITEM";
export const CART_REMOVE_ITEM = "CART_REMOVE_ITEM";
|
Create frontend/src/reducers/cartReducers.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
import { CART_ADD_ITEM, CART_REMOVE_ITEM } from "../constants/cartConstants";
export const cartReducer = (state = { cartItems: [] }, action) => {
switch (action.type) {
case CART_ADD_ITEM:
const item = action.payload;
const existItem = state.cartItems.find((x) => x.product === item.product);
if (existItem) {
return {
...state,
cartItems: state.cartItems.map((x) =>
x.product === existItem.product ? item : x
),
};
} else {
return {
...state,
cartItems: [...state.cartItems, item],
};
}
case CART_REMOVE_ITEM:
return {
...state,
cartItems: state.cartItems.filter((x) => x.product !== action.payload),
};
default:
return state;
}
};
|
Create frontend/src/actions/cartActions.js
axios
: when we add an item to the cart, we want to make a request to API products and then the id to get the data for that particular product to add to our cart
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
import axios from "axios";
import { CART_ADD_ITEM, CART_REMOVE_ITEM } from "../constants/cartConstants";
export const addToCart = (id, qty) => async (dispatch, getState) => {
const { data } = await axios.get(`/api/products/${id}`);
dispatch({
type: CART_ADD_ITEM,
payload: {
product: data._id,
name: data.name,
image: data.image,
price: data.price,
countInStock: data.countInStock,
qty,
},
});
localStorage.setItem("cartItems", JSON.stringify(getState().cart.cartItems));
};
export const removeFromCart = (id) => (dispatch, getState) => {
dispatch({
type: CART_REMOVE_ITEM,
payload: id,
});
localStorage.setItem("cartItems", JSON.stringify(getState().cart.cartItems));
};
|
Add cartReducer
in frontend/src/store.js
- get item by
cartItemsFromStorage
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
import {
productListReducer,
productDetailsReducer,
} from "./reducers/productReducers";
import { cartReducer } from "./reducers/cartReducers";
const reducer = combineReducers({
productList: productListReducer,
productDetails: productDetailsReducer,
cart: cartReducer,
});
const cartItemsFromStorage = localStorage.getItem("cartItems")
? JSON.parse(localStorage.getItem("cartItems"))
: [];
const initialState = {
cart: { cartItems: cartItemsFromStorage },
};
|
Create frontend/src/screens/CartScreen.js
- import to App.js and add routes
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
|
import React, { useEffect } from "react";
import { Link } from "react-router-dom";
import { useDispatch, useSelector } from "react-redux";
import {
Row,
Col,
ListGroup,
Image,
Form,
Button,
Card,
} from "react-bootstrap";
import Message from "../components/Message";
import { addToCart, removeFromCart } from "../actions/cartActions";
const CartScreen = ({ match, location, history }) => {
const productId = match.params.id;
//console.log(location.search);
// return ?qty=1
const qty = location.search ? Number(location.search.split("=")[1]) : 1;
const dispatch = useDispatch();
const cart = useSelector((state) => state.cart);
const { cartItems } = cart;
useEffect(() => {
if (productId) {
dispatch(addToCart(productId, qty));
}
}, [dispatch, productId, qty]);
const removeFromCartHandler = (id) => {
dispatch(removeFromCart(id));
};
const checkoutHandler = () => {
history.push("/login?redirect=shipping");
};
return (
<Row>
<Col md={8}>
<h1>Shopping Cart</h1>
{cartItems.length === 0 ? (
<Message>
Your cart is empty <Link to="/">Go Back</Link>
</Message>
) : (
<ListGroup variant="flush">
{cartItems.map((item) => (
<ListGroup.Item key={item.product}>
<Row>
<Col md={2}>
<Image src={item.image} alt={item.name} fluid rounded />
</Col>
<Col md={3}>
<Link to={`/products/${item.product}`}>{item.name}</Link>
</Col>
<Col md={2}>${item.price}</Col>
<Col md={2}>
<Form.Control
as="select"
value={item.qty}
onChange={(e) =>
dispatch(
addToCart(item.product, Number(e.target.value))
)
}
>
{[...Array(item.countInStock).keys()].map((x) => (
<option key={x + 1} value={x + 1}>
{x + 1}
</option>
))}
</Form.Control>
</Col>
<Col md={2}>
<Button
type="button"
variant="light"
onClick={() => removeFromCartHandler(item.product)}
>
<i className="fas fa-trash"></i>
</Button>
</Col>
</Row>
</ListGroup.Item>
))}
</ListGroup>
)}
</Col>
<Col md={4}>
<Card>
<ListGroup variant="flush">
<ListGroup.Item>
<h2>
Subtotal ({cartItems.reduce((acc, item) => acc + item.qty, 0)})
items
</h2>
${cartItems.reduce((acc, item) => acc + item.qty * item.price, 0).toFixed(2)}
</ListGroup.Item>
<ListGroup>
<Button
type="button"
className="btn-block"
disabled={cartItems.length === 0}
onClick={checkoutHandler}
>
Proceed To Checkout
</Button>
</ListGroup>
</ListGroup>
</Card>
</Col>
</Row>
);
};
export default CartScreen;
|