Skip to main content

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:

configurationSchema.json
[
{
"label": "Product",
"name": "product",
"type": "product"
}
]
index.tsx
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 action prop here to attach to - the block renders nothing (return null) and reacts purely to total changing, so product.actions.addToCart/a line item's actions.remove have to be fired from inside the useEffect itself, via remoteAction.do.
  • useCart() gives you the live cart - cart.totalPrice drives total, and cart.lineItems.find(...) locates the gift's own line item (if it's already been added) by matching item.product?.id against the picked gift product.
  • Guard for a missing action (giftProduct.actions?.addToCart, giftLineItem?.actions?.remove) before calling remoteAction.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 remoteAction entirely and just write <Button action={giftProduct.actions.addToCart}>Add gift</Button> - remoteAction.do only comes in once there's no prop to hand the action to.