Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
"use client";

import { useQuery } from "@tanstack/react-query";
import { useCallback, useEffect, useState } from "react";
import { trackPayEvent } from "../../../../../analytics/track/pay.js";
import type { Buy, Sell } from "../../../../../bridge/index.js";
import type { TokenWithPrices } from "../../../../../bridge/types/Token.js";
import type { ThirdwebClient } from "../../../../../client/client.js";
Expand Down Expand Up @@ -241,6 +243,17 @@ export type SwapWidgetProps = {
* @bridge
*/
export function SwapWidget(props: SwapWidgetProps) {
useQuery({
queryFn: () => {
trackPayEvent({
client: props.client,
event: "ub:ui:swap_widget:render",
});
return true;
},
queryKey: ["swap_widget:render"],
});

Comment on lines +246 to +256
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Do not use React Query to fire-and-forget analytics; this introduces a hard dependency on QueryClientProvider and can double-count or miss events.

  • This will throw at runtime in hosts that haven’t wrapped the app with QueryClientProvider.
  • Static queryKey dedupes across multiple widgets; default refetch/retry can re-fire on focus/reconnect/failure.

Replace with a one-time useEffect:

-  useQuery({
-    queryFn: () => {
-      trackPayEvent({
-        client: props.client,
-        event: "ub:ui:swap_widget:render",
-      });
-      return true;
-    },
-    queryKey: ["swap_widget:render"],
-  });
+  useEffect(() => {
+    void trackPayEvent({
+      client: props.client,
+      event: "ub:ui:swap_widget:render",
+    });
+  }, [props.client]);

If you must keep React Query, at minimum guard against re-fetches and retries and scope the key:

-  useQuery({
-    queryFn: () => {
+  useQuery({
+    queryFn: async () => {
       trackPayEvent({
         client: props.client,
         event: "ub:ui:swap_widget:render",
       });
-      return true;
+      return true;
     },
-    queryKey: ["swap_widget:render"],
+    queryKey: ["swap_widget:render", props.client],
+    staleTime: Infinity,
+    retry: false,
+    refetchOnWindowFocus: false,
+    refetchOnReconnect: false,
   });
🤖 Prompt for AI Agents
In packages/thirdweb/src/react/web/ui/Bridge/swap-widget/SwapWidget.tsx around
lines 246 to 256, the code uses useQuery to fire-and-forget analytics which
creates a hard dependency on QueryClientProvider and can double-count or miss
events; replace the useQuery block with a one-time useEffect that calls
trackPayEvent on mount (with proper dependency array) so it runs exactly once
and does not rely on React Query, or if you must keep React Query, use a unique,
instance-scoped queryKey and disable retries/refetches (set retry: false,
refetchOnWindowFocus: false, refetchOnReconnect: false) and return a stable
promise to ensure the event is not re-fired.

return (
<SwapWidgetContainer
theme={props.theme}
Expand Down
Loading