Executing Actions Programmatically
Passing an action value straight to <Button action={...}>/<Actionable action={...}> is all you need for a press handler - the component runs it for you. But some actions aren't triggered by a press at all - e.g. reacting to the cart changing, inside a useEffect. There's no action prop to wire in that case, so you need to run the action yourself: remoteAction (from @evlop/commons) does that - remoteAction.do(action) executes any action value imperatively, from anywhere in your code.
Example
A free-gift-with-purchase block: once the cart total crosses a threshold, automatically add a specific gift product; if the total drops back below it, automatically remove it again. Nothing here is a button press - it all reacts to the live cart total.
The gift product itself comes from a product settings panel field (see Showing a Merchant-Selected Product for more on this field type), included here for a complete, runnable example:
[
{
"label": "Product",
"name": "product",
"type": "product"
}
]
import React, { useEffect } from 'react';
import { useCart, CustomBlock } from '@evlop/shopify';
import { remoteAction } from '@evlop/commons';
const FREE_GIFT_THRESHOLD = 100;
const FreeGiftManager: CustomBlock.Section = ({ data }) => {
const cart = useCart();
const giftProduct = data?.product;
const total = +(cart?.totalPrice?.amount ?? 0);
useEffect(() => {
if (!giftProduct) return;
const giftLineItem = cart?.lineItems?.find((item) => item.product?.id === giftProduct.id);
const qualifies = total >= FREE_GIFT_THRESHOLD;
if (qualifies && !giftLineItem && giftProduct.actions?.addToCart) {
remoteAction.do(giftProduct.actions.addToCart);
}
if (!qualifies && giftLineItem?.actions?.remove) {
remoteAction.do(giftLineItem.actions.remove);
}
}, [total, giftProduct, cart?.lineItems]);
return null;
};
export default FreeGiftManager;
How it works
- No
actionprop here to attach to - the block renders nothing (return null) and reacts purely tototalchanging, soproduct.actions.addToCart/a line item'sactions.removehave to be fired from inside theuseEffectitself, viaremoteAction.do. useCart()gives you the live cart -cart.totalPricedrivestotal, andcart.lineItems.find(...)locates the gift's own line item (if it's already been added) by matchingitem.product?.idagainst the picked gift product.- Guard for a missing action (
giftProduct.actions?.addToCart,giftLineItem?.actions?.remove) before callingremoteAction.do- the product may not be picked yet, or no matching line item may exist. - Contrast with a plain button: if you only needed to add the gift on a press instead, you'd skip
remoteActionentirely and just write<Button action={giftProduct.actions.addToCart}>Add gift</Button>-remoteAction.doonly comes in once there's no prop to hand the action to.