DEV Community

Cover image for React Hooks Snippet: Shopping Cart
Ryan Kahn (he/him)
Ryan Kahn (he/him)

Posted on • Edited on

14 7

React Hooks Snippet: Shopping Cart

Hey all! How would you model a shopping cart with React Hooks? Here's how I would do it!

The main things to look at:

  • This is written in Typescript, to assist the gist also contains the same code in JavaScript.
  • The Types!
    • An order is Tuple of an ID (which is a string) and a Quantity (which is a number).
    • The Cart is a Record, which is an object where the keys are the item IDs and the values are their Quantity.
  • The reducer for useReducer doesn't take a Flux Standard Action! 🤯😱 Let's keep things less complex! Here our reducer is just taking our Order tuples, and reducing the Cart from that.
  • We have two effects we run in our useShoppingCart hook.
    • First, we fetch the saved cart from the server, and add all those items to the cart
    • Second, every time the cart updates we POST that to the server. We keep track of the saved and error status with useState.
    • Could we save the cart before we fetch the cart? I dunno! Maybe?

If shopping carts aren't your thing, but you like this style, leave a comment with what hooks snippet I should write next!

import { useReducer, useState, useEffect } from "react";
type ID = string;
type Quantity = number;
type Order = [ID, Quantity];
type Cart = Record<ID, Quantity>;
const reduceCartFromOrders = (current: Cart, [id, quantity]: Order) => {
if (current === null) current = {};
if (quantity === null) {
delete current[id];
return current;
}
const currentQuantity = current[id] || 0;
const newQuantity = Math.max(currentQuantity + quantity, 0);
return {
...current,
[id]: newQuantity
};
};
export function useShoppingCart(userId: string) {
const [cart, processOrder]: [Cart, (o: Order) => void] = useReducer(
reduceCartFromOrders,
null
);
const addItem = id => processOrder([id, 1]);
const subtractItem = id => processOrder([id, -1]);
const removeItem = id => processOrder([id, null]);
const [error, setError] = useState<string>(null);
const [saved, setSaved] = useState<boolean>(false);
useEffect(() => {
if (userId !== null) {
fetch(`/cart/${userId}`)
.then(resp => resp.json())
.then(
initialCart =>
Object.keys(initialCart).forEach(id =>
processOrder([id, initialCart[id]])
)
)
.catch(error => {
setError(error.toString());
});
}
}, [userId]);
useEffect(() => {
if (userId !== null && cart !== null) {
setSaved(false);
fetch(`/cart/${userId}`, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(cart)
})
.then(() => {
setSaved(true);
setError(null);
})
.catch(error => {
setSaved(false);
setError(error.toString());
});
}
}, [cart, userId]);
return {
cart,
error,
saved,
processOrder,
addItem,
subtractItem,
removeItem
};
}
view raw cart.ts hosted with ❤ by GitHub

SurveyJS custom survey software

JavaScript UI Libraries for Surveys and Forms

SurveyJS lets you build a JSON-based form management system that integrates with any backend, giving you full control over your data and no user limits. Includes support for custom question types, skip logic, integrated CCS editor, PDF export, real-time analytics & more.

Learn more

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs