I am find problem in persisting data when page is refreshed. I've a file with reducer and actions. I'm using shopify-js-buy sdk for cart of my project. The cart works fine if there is not page refresh but when there is a page refresh cart item data is not persisting. I feel that if I use only open/close state is persisted but other data like subtotal , items, no.of items etc. are not persisting.
this is reducer and action file:
import { useSelector, useDispatch } from "react-redux"
import Client from "shopify-buy"
// Creates the client with Shopify-Buy and store info
//Example Storefront
const client = Client.buildClient({
storefrontAccessToken: "dd4d4dc146542ba7763305d71d1b3d38",
domain: "graphql.myshopify.com",
})
const PRODUCTS_FOUND = "shopify/PRODUCTS_FOUND"
const PRODUCT_FOUND = "shopify/PRODUCT_FOUND"
const COLLECTION_FOUND = "shopify/COLLECTION_FOUND"
const CHECKOUT_FOUND = "shopify/CHECKOUT_FOUND"
const SHOP_FOUND = "shopify/SHOP_FOUND"
const ADD_VARIANT_TO_CART = "shopify/ADD_VARIANT_TO_CART"
const UPDATE_QUANTITY_IN_CART = "shopify/UPDATE_QUANTITY_IN_CART"
const REMOVE_LINE_ITEM_IN_CART = "shopify/REMOVE_LINE_ITEM_IN_CART"
const OPEN_CART = "shopify/OPEN_CART"
const CLOSE_CART = "shopify/CLOSE_CART"
const CART_COUNT = "shopify/CART_COUNT"
const initialState = {
isCartOpen: false,
cartCount: 0,
checkout: {},
products: [],
featured: [],
product: {},
shop: {},
}
export default (state = initialState, action) => {
switch (action.type) {
case PRODUCTS_FOUND:
return { ...state, products: action.payload }
case PRODUCT_FOUND:
return { ...state, product: action.payload }
case COLLECTION_FOUND:
return { ...state, featured: action.payload }
case CHECKOUT_FOUND:
return { ...state, checkout: action.payload }
case SHOP_FOUND:
return { ...state, shop: action.payload }
case ADD_VARIANT_TO_CART:
return { ...state, checkout: action.payload }
case UPDATE_QUANTITY_IN_CART:
return { ...state, checkout: action.payload }
case REMOVE_LINE_ITEM_IN_CART:
return { ...state, checkout: action.payload }
case OPEN_CART:
return { ...state, isCartOpen: true }
case CLOSE_CART:
return { ...state, isCartOpen: false }
case CART_COUNT:
return { ...state, cartCount: action.payload }
default:
return state
}
}
// Gets all the products from Shopify
export function getProducts() {
return (dispatch) => {
client.product.fetchAll().then((resp) => {
dispatch({
type: PRODUCTS_FOUND,
payload: resp,
})
})
}
}
// Gets individual item based on id
export function getProduct(id) {
return async (dispatch) => {
const resp = await client.product.fetch(id)
dispatch({
type: PRODUCT_FOUND,
payload: resp,
})
return resp
}
}
// Creates initial checkout state from Shopify
export function checkout() {
return (dispatch) => {
client.checkout.create().then((resp) => {
dispatch({
type: CHECKOUT_FOUND,
payload: resp,
})
})
}
}
// Gets Shopify store information
export function shopInfo() {
return (dispatch) => {
client.shop.fetchInfo().then((resp) => {
dispatch({
type: SHOP_FOUND,
payload: resp,
})
})
}
}
// Adds variants to cart/checkout
export function addVariantToCart(checkoutId, lineItemsToAdd) {
return async (dispatch) => {
const response = await client.checkout.addLineItems(
checkoutId,
lineItemsToAdd
)
dispatch({
type: ADD_VARIANT_TO_CART,
payload: response,
})
return response
}
}
// Updates quantity of line items in cart and in checkout state
export function updateQuantityInCart(lineItemId, quantity, checkoutId) {
const lineItemsToUpdate = [
{ id: lineItemId, quantity: parseInt(quantity, 10) },
]
return async (dispatch) => {
const resp = await client.checkout.updateLineItems(
checkoutId,
lineItemsToUpdate
)
dispatch({
type: UPDATE_QUANTITY_IN_CART,
payload: resp,
})
return resp
}
}
// Removes line item from cart and checkout state
export function removeLineItemInCart(checkoutId, lineItemId) {
return (dispatch) => {
client.checkout.removeLineItems(checkoutId, [lineItemId]).then((resp) => {
dispatch({
type: REMOVE_LINE_ITEM_IN_CART,
payload: resp,
})
})
}
}
// To close the cart
export function handleCartClose() {
return {
type: CLOSE_CART,
}
}
// To open the cart
export function handleCartOpen() {
return {
type: OPEN_CART,
}
}
// Set the count of items in the cart
export function handleSetCount(count) {
return {
type: CART_COUNT,
payload: count,
}
}
// this is exporting reducer file as shopifyState
export { default as shopifyState } from "./Shopify" //this written in another file.
//this store.js
//===================================================================================
import { createStore, combineReducers, applyMiddleware, compose } from "redux"
import thunk from "redux-thunk";
import * as reducers from "../ReduxStore/ShopifyCart";
import { persistStore, persistReducer } from 'redux-persist';
import storage from 'redux-persist/lib/storage';
import autoMergeLevel2 from 'redux-persist/lib/stateReconciler/autoMergeLevel2';
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose
const rootReducer = combineReducers(reducers)
const enhancer = composeEnhancers(applyMiddleware(thunk))
const persistConfig = {
key: 'root',
storage,
stateReconciler: autoMergeLevel2,
}
const persistedReducer = persistReducer(persistConfig, rootReducer)
export const Store = createStore(persistedReducer, undefined, enhancer)
export const PersistStore = persistStore(Store);
//this is index.js
ReactDOM.render(
<BrowserRouter>
<Provider store={Store}>
<PersistGate loading={null} persistor={PersistStore}>
<React.StrictMode>
<App />
</React.StrictMode>
</PersistGate>
</Provider>
</BrowserRouter>,
document.getElementById('root')
);
//this file contains code for showing specific product based on input from another file
import React, { useEffect, useState } from "react"
import { useDispatch, useSelector } from 'react-redux';
import { Link } from "react-router-dom"
// import { useShopify } from "../../../../hooks"
import {addVariantToCart,getProduct,handleCartOpen} from '../../../../ReduxStore/ShopifyCart/Shopify'
export default (props) => {
const dispatch = useDispatch()
const checkoutState = useSelector(
(state) => state.shopifyState.checkout
)
const product = useSelector(
(state)=>(state.shopifyState.product)
)
const id = props.match.params.productId
const defaultSize = product.variants && product.variants[0].id.toString()
const [size, setSize] = useState("")
const [quantity, setQuantity] = useState(1)
const description = product.description && product.description.split(".")
function changeSize(sizeId, quantity) {
dispatch(handleCartOpen())
if (sizeId === "") {
sizeId = defaultSize
const lineItemsToAdd = [
{ variantId: sizeId, quantity: parseInt(quantity, 10) },
]
const checkoutId = checkoutState.id
// addVariant(checkoutId, lineItemsToAdd)
dispatch(addVariantToCart(checkoutId, lineItemsToAdd))
} else {
const lineItemsToAdd = [
{ variantId: sizeId, quantity: parseInt(quantity, 10) },
]
const checkoutId = checkoutState.id
// addVariant(checkoutId, lineItemsToAdd)
dispatch(addVariantToCart(checkoutId, lineItemsToAdd))
}
}
useEffect(() => {
(dispatch(getProduct(id)))
}, [id])
return (
<div id="individualProduct">
<Link className="homeButton button" to={"/Home"}>
Home
</Link>
<div className="Product-wrapper2">
<div className="Images">
{product.images &&
product.images.map((image, i) => {
return (
<img
key={image.id + i}
src={image.src}
alt={`${product.title} product shot`}
/>
)
})}
</div>
<div className="Product__info">
<h2 className="Product__title2">{product.title}</h2>
<ul className="Product__description">
{description &&
description.map((each, i) => {
return <li key={`line-description +${i}`}>{each}</li>
})}
</ul>
<div>
<label htmlFor={"prodOptions"}>Size</label>
<select
id="prodOptions"
name={size}
onChange={(e) => {
setSize(e.target.value)
}}
>
{product.variants &&
product.variants.map((item, i) => {
return (
<option
value={item.id.toString()}
key={item.title + i}
>{`${item.title}`}</option>
)
})}
</select>
</div>
<div>
<label>Quantity</label>
<input
className="quantity"
type="number"
min={1}
value={quantity}
onChange={(e) => {
setQuantity(e.target.value)
}}
></input>
</div>
<h3 className="Product__price">
${product.variants && product.variants[0].price}
</h3>
<button
className="prodBuy button"
onClick={(e) => changeSize(size, quantity)}
>
Add to Cart
</button>
</div>
</div>
</div>
)
}
//this is cart code
import React, { useEffect } from "react"
import LineItem from "../LineItem/LineItem"
// import { useShopify } from "../../../../hooks"
import { useSelector, useDispatch } from "react-redux"
import {handleCartOpen,handleCartClose,handleSetCount} from '../../../../ReduxStore/ShopifyCart/Shopify'
// import { MdShoppingCart, MdRemoveShoppingCart } from "react-icons/md"
export default (props) => {
const cartStatus = useSelector((appState) => appState.shopifyState.isCartOpen)
const checkoutState = useSelector(
(appState) => appState.shopifyState.checkout
)
const dispatch = useDispatch()
function handleOpen(e) {
e.preventDefault()
dispatch(handleCartOpen())
}
function handleClose(e) {
e.preventDefault()
dispatch(handleCartClose())
}
function openCheckout(e) {
e.preventDefault()
// window.open(checkoutState.webUrl) // opens checkout in a new window
window.location.replace(checkoutState.webUrl) // opens checkout in same window
}
useEffect(() => {
const button = document.querySelector("button.App__view-cart")
if (cartStatus === true) {
button.classList.add("hide")
} else {
button.classList.remove("hide")
}
function getCount() {
let lineItems =
checkoutState.lineItems && checkoutState.lineItems.length > 0
? checkoutState.lineItems
: []
let count = 0
lineItems.forEach((item) => {
count += item.quantity
return count
})
dispatch(handleSetCount(count))
}
getCount()
}, [cartStatus, checkoutState])
return (
<div id="cart">
<div className={`Cart ${cartStatus ? "Cart--open" : ""}`}>
<div className="App__view-cart-wrapper2">
<button className="App__view-cart" onClick={(e) => handleOpen(e)}>
{/* <MdShoppingCart /> */}
</button>
</div>
<header className="Cart__header">
<h2>Your cart</h2>
<button className="Cart__close" onClick={(e) => handleClose(e)}>
{/* <MdRemoveShoppingCart /> */}
<span style={{fontSize: '30px'}}>X</span>
</button>
</header>
<ul className="Cart__line-items">
<LineItem />
</ul>
<footer className="Cart__footer">
<div className="Cart-info clearfix">
<div className="Cart-info__total Cart-info__small">Subtotal</div>
<div className="Cart-info__pricing">
<span className="pricing">$ {checkoutState.subtotalPrice}</span>
</div>
</div>
<div className="Cart-info clearfix">
<div className="Cart-info__total Cart-info__small">Taxes</div>
<div className="Cart-info__pricing">
<span className="pricing">$ {checkoutState.totalTax}</span>
</div>
</div>
<div className="Cart-info clearfix">
<div className="Cart-info__total Cart-info__small">Total</div>
<div className="Cart-info__pricing">
<span className="pricing">$ {checkoutState.totalPrice}</span>
</div>
</div>
<button
className="Cart__checkout button"
onClick={(e) => openCheckout(e)}
>
Checkout
</button>
</footer>
</div>
</div>
)
}
I'm working on reactJs. Will be thankfull for any help on this .