Shopping cart
In this recipe, we'll look at how to build a global shopping cart state. One that can be used in a header for a count of all quantity, a shopping cart panel, optimistic updates, etc. Here's how to achieve that:
Render the cart in your props across all pages in your application.json.props
and mark it as a fragment.
json.data do
json.cart partial: ['cart', fragment: true] do
end
yield
end
Add a slice
import { createSlice, createAction } from '@reduxjs/toolkit'
import { updateFragments } from '@thoughtbot/superglue'
export const cartSlice = createSlice({
name: 'cart',
initialState: {},
reducers: {
addToCart: (state, action) => {
....logic to add something to the cart ...
}
},
extraReducers: (builder) => {
builder.addCase(updateFragments, (state, action) => {
// Update the slice with the latest and greatest.
return action.value
})
}
})
With fragment
enabled, the above will populate the slice whenever a page is received, while allowing you the flexibility to make local edits using the custom addToCart
reducer.
You can use this cart slice as you normally would with Redux selectors
// For the cart component
const cart = useSelector(state => state.cart)
// For a header quantity component
const cartCount = cart.lineItems.reduce((memo, line) => memo + line.qty, 0)
For updates to the backend, add a ujs attribute to a normal form.
<form action='/add_to_cart?props_at=data.header.cart' method='POST' data-sg-remote={true}>
def create
... add to cart logic here...
# This helper will retain the `props_at` param when redirecting, which allows the
# partial rendering of the `show` page.
redirect_back_with_props_at fallback_url: '/'
end
The above will POST
, and get redirected back to the original page while fetching only the cart to update. This will be picked up by extraReducers
and update the entire cart state.
Last updated
Was this helpful?