Hydrogen: How do you recieve the action data when you submit a CartForm?

So we are trying to do some validation for our cart using the barebones Hydrogen quickstart in TypeScript, but we are having issues using the CartForm component.

Whenever you submit a cart form to update some attributes, Hydrogen uses the component like a form.

// in app/components/Cart.tsx
function CartLineUpdateButton({
  children,
  lines,
}: {
  children: React.ReactNode;
  lines: CartLineUpdateInput[];
}) {
  return (
    <CartForm
      route="/cart"
      action={CartForm.ACTIONS.LinesUpdate}
      inputs={{lines}}
    >
      {children}
    </CartForm>
  );
}

By default, we are given an action in app/routes/($locale).cart.tsx that receives the submitted information.

// in app/routes/($locale).cart.tsx
export async function action({request, context}: ActionFunctionArgs) {
  ...
  return json(
    {
      cart: cartResult,
      errors,
      analytics: {
        cartId,
      },
    },
    {status, headers},
  );

Typically in React, the action returns information to whatever route submitted the form with “useActionData”. You can see an example in the official React documentation: https://reactrouter.com/en/main/hooks/use-action-data#useactiondata

export default function SignUp() {
  const errors = useActionData();

  return (

    <Form method="post">

     ...

      <p>
        <button type="submit">Sign up</button>
      </p>
    </Form>
  );
}

However, when attempting to receive information from the built-in action in app/routes/($locale).cart.tsx, the value always comes back as “undefined”. This is even after returning to the page, forcing a reload.

// in app/routes/($locale).cart.tsx

export default function Cart() {
  const actionData = useActionData();
  console.log("action data: " + actionData); // always undefined

How can we receive this information? Where does it go? We would like to use the “errors” value for some form validation, and display any errors. But without the action’s return value, we can never display anything in addition. We could use some help figuring this out.

You can try adding a “fetcherKey” to the Form and then use the “useFetcher” hook with that key to get the result of the form action.

https://remix.run/docs/en/main/components/form#fetcherkey
https://remix.run/docs/en/main/hooks/use-fetcher#key

Adding a fetcherKey only gives me the result {“Form”:{},“state”:“idle”} , which is not the format as the action.

function CartLineUpdateButton({
  children,
  lines,
}: {
  children: React.ReactNode;
  lines: CartLineUpdateInput[];
}) {
  const fetcher = useFetcher({key: "cart-increment"});
  
  console.log(JSON.stringify(fetcher));

  return (
    
  );
}

What is this information, and how can we get the component to update when the form is submitted without refreshing the page? Is an effect required?

You can try to retrieve the “fetcher.data” result with “useEffect”

function CartLineUpdateButton({
  children,
  lines,
}: {
  children: React.ReactNode;
  lines: CartLineUpdateInput[];
}) {
  const fetcher = useFetcher({key: "cart-increment"});
  
  useEffect(() => {
    console.log('fetcher.data', fetcher.data)
  }, [fetcher.data])

  return (
    
  );
}

That still won’t work. The effect never updates, and fetcher from “cart-increment” is always ‘{“Form”:{},“state”:“idle”}’, even when it fails to update.

Hmm, I think you can try this hook: https://github.com/Weaverse/pilot/blob/main/app/hooks/useCartFetchers.tsx

It looks like I was checking the value of the fetcher through a console.log in the code, not in the returned JSX.Element. When I use the fetcher in the element, everything displays correctly.

const fetcher = useFetcher({key: 'cart-increment'});
  console.log(JSON.stringify(fetcher)); // "{"Form":{},"state":"idle"}"
  return (
    
  );

It looks like this gets the information that the form submits, but not necessarily the information we were looking for.

We’re still trying to obtain the returning information from the Cart “Action”. I don’t know how to access and display this information on the front-end:

return json(
    {
      cart: cartResult,
      errors,
      analytics: {
        cartId,
      },
    },
    {status, headers},
  );