Created
November 5, 2019 17:33
-
-
Save notrab/bac9f854c95757e5713fd78677e2494b to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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