-
Notifications
You must be signed in to change notification settings - Fork 263
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add the useOptimisticCart
hook
#2069
Conversation
Oxygen deployed a preview of your
Learn more about Hydrogen's GitHub integration. |
@@ -67,16 +69,25 @@ type CartDiscountCodesUpdateRequire = { | |||
} & OtherFormData; | |||
}; | |||
|
|||
export type OptimisticCartLine = CartLineInput & { | |||
selectedVariant?: unknown; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm trying to figure out the right type to set here. It's tricky. ProductVariant
does not work.
582bb03
to
9789710
Compare
import type {CartLineUpdateInput} from '@shopify/hydrogen/storefront-api-types'; | ||
import {Link} from '@remix-run/react'; | ||
import type {CartApiQueryFragment} from 'storefrontapi.generated'; | ||
import {useVariantUrl} from '~/lib/variants'; | ||
|
||
type CartLine = CartApiQueryFragment['lines']['nodes'][0]; | ||
type CartLine = OptimisticCart<CartApiQueryFragment>['lines']['nodes'][0]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The OptimisticCart
type augments the normal cart fragment to include the isOptimistic
properties.
@@ -184,7 +208,7 @@ function CartLineQuantity({line}: {line: CartLine}) { | |||
<CartLineUpdateButton lines={[{id: lineId, quantity: prevQuantity}]}> | |||
<button | |||
aria-label="Decrease quantity" | |||
disabled={quantity <= 1} | |||
disabled={quantity <= 1 || !!isOptimistic} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Disable the cart line action buttons when line.isOptimistic
is true. isOptimistic
only goes to true when it's an entirely new line. So a new item in the cart that hasn't synced yet to the server. Actions cannot be taken because there is no lineId
yet. But other optimistic actions, like changing the line quantity with LinesUpdate
, line.isOptimistic
stays false. This allows the user to rapidly click the +1 button to add many to the cart. The root cart.isOptimistic
does get set to true
. So the dev could still show a generic visual indicator for the whole cart until all actions are resolved.
4324854
to
c08eb4d
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Love this, great work @blittle.
Side note:
I noticed a layout shift because the price is not optimistic. I would maybe add adding
or $0
to prevent the vertical shift
cart-layout-shift.mp4
Probably unrelated, but if you notice closely there's also a flash on the already rendered line item's image when the cart become non-optimistic.
db69916
to
988f298
Compare
Yeah I was wondering about this, there are a few problems with us setting a currency:
Instead I think it's better to just use the |
58d48f2
to
4c19725
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This works great Bret! And a really simple API 🔥 🚢
I was noticing that the images in the cart blink when requests are resolved but then realized it's because I was disabling browser cache. On React re-render it was probably downloading a different image even though the URL was the same. So no issue for us :)
const cartId = result.cart.id; | ||
const headers = cart.setCartId(result.cart.id); | ||
const cartId = result?.cart?.id; | ||
const headers = cartId ? cart.setCartId(result.cart.id) : new Headers(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was thinking maybe new Headers()
would break SSR in Node.js... but it was added in v18 globally, which is our minimum required version 😄
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great work Bret and thanks for incorporating the various feedbacks!
WHY are these changes introduced?
An optimistic cart makes cart actions immediately render in the browser while the action syncs to the server. This increases the perceived performance of the application.
WHAT is this pull request doing?
This PR adds the
useOptimisticCart()
hook. This hook takes the cart object as a parameter, and processes all pending cart actions, locally mutating the cart with optimistic state. Once the pending cart actions resolve, cart object should automatically be updated from the server, anduseOptimisticCart()
just no-ops.See the changeset for a deeper explanation and example.
HOW to test your changes?
Post-merge steps
Checklist