Skip to content

Instantly share code, notes, and snippets.

@shiftyp
Last active June 17, 2020 07:22
Show Gist options
  • Save shiftyp/c0ae8c8b8eda947ddb740449bb92ebb4 to your computer and use it in GitHub Desktop.
Save shiftyp/c0ae8c8b8eda947ddb740449bb92ebb4 to your computer and use it in GitHub Desktop.
A shopping cart with server persistence built with React hooks
import { useReducer, useState, useEffect } from "react";
const reduceCartFromOrders = (current, [id, quantity]) => {
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) {
const [cart, processOrder] = 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(null);
const [saved, setSaved] = useState(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
};
}
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
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment