The new POS navigation API is unusable

Hi @Paige-Shopify

Thanks for looking into this.

Sorry for leaving some details out. The issue appears to be more subtle. Please see the snippet below.

export function Example() {
  useEffect(() => {
    const unsubscribeScanner = shopify.scanner.scannerData.current.subscribe(
      async ({ data }) => {
        if (!data) return;

        shopify.toast.show(`Scanned: "${data}"`);
        navigation.back();
        // It works when calling navigation.navigate instead of navigation.back. Why is navigate async, but back is not?
        // await navigation.navigate("/");

        try {
          await shopify.cart.setCustomer({
            id: Number(data),
          });
          shopify.toast.show(`Customer set successfully`);
        } catch {
          shopify.toast.show(`Error setting customer`);
        }
      }
    );

    return () => unsubscribeScanner();
  }, []);

  // Go to the camera page and wait for the timeout - it works as expected
  // useEffect(() => {
  //   const timeout = setTimeout(async () => {
  //     navigation.back();

  //     try {
  //       await shopify.cart.setCustomer({
  //         id: <customer ID here>,
  //       });
  //       shopify.toast.show(`Customer set successfully`);
  //     } catch {
  //       shopify.toast.show(`Error setting customer`);
  //     }
  //   }, 5000);
  //   return () => clearTimeout(timeout);
  // }, []);

  if (navigation.currentEntry.url === "/camera") {
    return <CameraPage />;
  }

  return (
    <s-page heading="Default page">
      <s-button onClick={() => navigation.navigate("/camera")}>
        Open camera page
      </s-button>
    </s-page>
  );
}

function CameraPage() {
  useEffect(() => {
    shopify.scanner.showCameraScanner();
    return () => {
      shopify.scanner.hideCameraScanner();
    };
  }, []);

  return <s-page heading="Camera page"></s-page>;
}

setCustomer doesn’t resolve when navigating back after a camera scan event. This is the actual issue. I think the error in the description is a red herring.

It works fine when calling await navigation.navigate(“/”) instead of navigation.back(), but it adds an entry to the navigation history stack, which I’d like to avoid. I’m curious why navigate returns a promise, but back doesn’t.

Interestingly, it also seems to work fine when navigating back and setting a customer after a timeout instead of a scan event. I’m still trying to wrap my head around how the new navigation works. I’ve noticed that sharing global state between views no longer works. Example:

export function ExampleTwo() {
  const [count, setCount] = useState(0);

  if (navigation.currentEntry.url === "/two") {
    return (
      <s-page heading="Two page">
        <s-button onClick={() => navigation.back()}>Go back</s-button>
        <s-text>Count: {count}</s-text>
        <s-button onClick={() => setCount((c) => c + 1)}>Increment</s-button>
      </s-page>
    );
  }

  return (
    <s-page heading="Page one">
      <s-button onClick={() => navigation.navigate("/two")}>
        Go to page two
      </s-button>

      <s-text>Count: {count}</s-text>
      <s-button onClick={() => setCount((c) => c + 1)}>Increment</s-button>
    </s-page>
  );
}

When navigating to page two, the count always resets. I wonder if setCustomer doesn’t resolve because it’s being called by the no longer active view? Whereas the timeout example works, because the timeout was initiated by the initial view.

It’d be really nice if there was any documentation explaining how it all works under the hood.