Skip to content

Instantly share code, notes, and snippets.

@notrab
Created November 5, 2019 17:33
Show Gist options
  • Save notrab/bac9f854c95757e5713fd78677e2494b to your computer and use it in GitHub Desktop.
Save notrab/bac9f854c95757e5713fd78677e2494b to your computer and use it in GitHub Desktop.
import React, { createContext, useContext, useReducer } from 'react'
const EMPTY_CART = 'EMPTY_CART'
const ADD_ITEM = 'ADD_ITEM'
const UPDATE_ITEM = 'UPDATE_ITEM'
const REMOVE_ITEM = 'REMOVE_ITEM'
const CartContext = createContext()
const defaultCartState = {
items: [],
totalItems: 0,
totalUniqueItems: 0,
}
function reducer(state, action) {
switch (action.type) {
case EMPTY_CART: {
return defaultCartState
}
case ADD_ITEM:
return {
...state,
items: [
...state.items,
{
...action.product,
quantity: action.quantity,
},
],
}
case UPDATE_ITEM:
return {
...state,
items: state.items.map(item => {
if (item.id !== action.id) return item
return {
...item,
...action.payload,
}
}),
}
case REMOVE_ITEM:
return {
...state,
items: state.items.filter(i => i.id !== action.id),
}
default:
throw new Error('No action specified')
}
}
export function CartProvider({ children, id, initialState }) {
if (!id) {
throw new Error('You must set an `id` when mounting the CartProvider')
}
const [state, dispatch] = useReducer(reducer, {
id,
...defaultCartState,
...initialState,
})
const totalUniqueItems = state.items.length
const emptyCart = () =>
dispatch({
type: EMPTY_CART,
})
const addItem = (product, quantity = 1) => {
if (quantity <= 0) return
const currentItem = state.items.find(item => item.id === product.id)
if (!currentItem) return dispatch({ type: ADD_ITEM, product, quantity })
dispatch({
type: UPDATE_ITEM,
id: product.id,
payload: {
...product,
quantity: currentItem.quantity + quantity,
},
})
}
const updateItem = (id, payload) =>
dispatch({ type: UPDATE_ITEM, id, payload })
const removeItem = id => dispatch({ type: REMOVE_ITEM, id })
const incrementItemQuantity = (id, by = 1) => {
if (by <= 0) return
const currentItem = state.items.find(item => item.id === id)
if (!currentItem) throw new Error('No such item to update')
const quantity = currentItem.quantity + by
if (quantity <= 0) return dispatch({ type: REMOVE_ITEM, id })
dispatch({
type: UPDATE_ITEM,
id,
payload: {
...currentItem,
quantity,
},
})
}
const decrementItemQuantity = (id, by = 1) => {
if (by <= 0) return
const currentItem = state.items.find(item => item.id === id)
if (!currentItem) throw new Error('No such item to update')
const quantity = currentItem.quantity - by
if (quantity <= 0) return dispatch({ type: REMOVE_ITEM, id })
dispatch({
type: UPDATE_ITEM,
id,
payload: {
...currentItem,
quantity,
},
})
}
const inCart = id => state.items.some(i => i.id === id)
return (
<CartContext.Provider
value={{
id,
items: state.items,
isEmpty: totalUniqueItems === 0,
totalItems: state.items.reduce((sum, i) => sum + i.quantity, 0),
totalUniqueItems,
inCart,
emptyCart,
addItem,
updateItem,
removeItem,
incrementItemQuantity,
decrementItemQuantity,
}}
>
{children}
</CartContext.Provider>
)
}
export const useCart = () => useContext(CartContext)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment