Payments — Before/After Demo

← Home

Side-by-side comparison of detecting a deeply nested payment SDK (acmePayments.ui.components, 3 levels deep). The left column uses the traditional setTimeout polling approach. The right column uses a single useWatchWindot call. Both render a live promo widget when the SDK is ready. The third-party script tag is loaded separately in the page layout and is not shown in the code snippets below.

Without windotwatchr

You've probably written this before. A setTimeout loop checking if the SDK exists every 100ms, racing against a script.onload that fires before the API is actually initialized. It works eventually, but it's wasteful and timing-dependent.

function BNPLPromo({ amount }) {
  const [ready, setReady] = useState(false);
  const ref = useRef(null);

  useEffect(() => {
    const check = () => {
      if (window?.acmePayments?.ui?.components) {
        setReady(true);
        ref.current = acmePayments.ui.components
          .create('promo', { amount, pageType: 'product' });
        ref.current.render('#container');
      } else {
        setTimeout(check, 100); // poll forever
      }
    };
    check();
    return () => { ref.current = null; };
  }, [amount]);

  if (!ready) return <p>Loading...</p>;
  return <div id="container" />;
}

Live demo

Polling every 100ms... (0 polls)

With windotwatchr

windotwatchr installs a lightweight Proxy trap on the window property path. The moment the SDK assigns its API, your callback fires immediately. No polling loops, no race conditions, no wasted CPU cycles.

function BNPLPromo({ amount }) {
  const { value: components, status } =
    useWatchWindot('acmePayments.ui.components');

  useEffect(() => {
    if (components) {
      components
        .create('promo', { amount, pageType: 'product' })
        .render('#container');
    }
  }, [components, amount]);

  return (
    <>
      <p>Status: {status}</p>
      <div id="container" />
    </>
  );
}

Live demo

Status: watching (zero-polling)