@dcl/hooks 2.0.3 → 2.0.4-20261008193059.commit-428d6ed

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -11,13 +11,23 @@ const KEEPALIVE_DELIVERY = {
11
11
  };
12
12
  const AnalyticsContext = createContext(null);
13
13
  const AnalyticsProvider = (props) => {
14
- const { writeKey, userId, traits, cdnUrl, apiHost, children } = props;
14
+ const { writeKey, userId, traits, cdnUrl, apiHost, deliveryStrategy, children, } = props;
15
15
  const analyticsRef = useRef(null);
16
+ // Kept out of the effect's dependencies: callers tend to pass it as an inline object, which would
17
+ // reload analytics on every render. It only matters at load time.
18
+ // Synced in an effect declared before the load effect, which runs first, so a render React discards
19
+ // never leaks its value into a load.
20
+ const deliveryStrategyRef = useRef(deliveryStrategy);
21
+ useEffect(() => {
22
+ deliveryStrategyRef.current = deliveryStrategy;
23
+ });
16
24
  // Identifies the run that owns the instance. Advanced synchronously on every run AND on every
17
25
  // cleanup, so a load still awaiting its import when the provider is reconfigured or unmounted sees a
18
26
  // stale generation and abandons instead of taking over.
19
27
  const generationRef = useRef(0);
20
- const [isInitialized, setIsInitialized] = useState(false);
28
+ // `loading`: the instance exists and buffers calls, but analytics.js has not fetched its settings and
29
+ // registered its plugins yet. `ready`: it has, and `isInitialized` reports true.
30
+ const [status, setStatus] = useState("idle");
21
31
  useEffect(() => {
22
32
  const generation = ++generationRef.current;
23
33
  const isCurrent = () => generation === generationRef.current;
@@ -43,15 +53,15 @@ const AnalyticsProvider = (props) => {
43
53
  if (resolvedCdnUrl) {
44
54
  settings.cdnURL = resolvedCdnUrl;
45
55
  }
46
- // keepalive lets an event fired right before a navigation (a link click that loads the next
47
- // page in the same tab) finish sending after the page unloads, instead of being cancelled
48
- // with it. analytics-next leaves it off by default.
56
+ // keepalive, the default here, lets an event fired right before a navigation (a link click that
57
+ // loads the next page in the same tab) finish sending after the page unloads, instead of being
58
+ // cancelled with it. analytics-next leaves it off by default.
49
59
  const resolvedApiHost = resolveApiHost(apiHost);
50
60
  const options = {
51
61
  integrations: {
52
62
  [SEGMENT_IO]: {
53
63
  ...(resolvedApiHost ? { apiHost: resolvedApiHost } : {}),
54
- deliveryStrategy: KEEPALIVE_DELIVERY,
64
+ deliveryStrategy: deliveryStrategyRef.current ?? KEEPALIVE_DELIVERY,
55
65
  },
56
66
  },
57
67
  };
@@ -59,16 +69,29 @@ const AnalyticsProvider = (props) => {
59
69
  if (userId) {
60
70
  analytics.identify(userId, traits);
61
71
  }
62
- // Published last: everything above is synchronous, so a throw leaves neither the ref nor
63
- // the registry holding an instance this run never finished setting up.
72
+ // Published once everything above, which is synchronous, succeeded: a throw leaves neither
73
+ // the ref nor the registry holding an instance this run never finished setting up.
64
74
  analyticsRef.current = analytics;
65
75
  registerAnalyticsInstance(analytics);
66
- setIsInitialized(true);
76
+ setStatus("loading");
77
+ // The instance buffers calls straight away but only sends them once analytics.js has fetched
78
+ // its settings and registered its plugins. Reporting ready before that tells a caller that an
79
+ // event fired right before a navigation will go out, while it can still be sitting in the
80
+ // buffer when the page unloads.
81
+ await analytics;
82
+ if (isCurrent()) {
83
+ setStatus("ready");
84
+ }
67
85
  }
68
86
  catch (error) {
69
87
  console.error("[Analytics] Failed to initialize:", error);
70
88
  if (isCurrent()) {
89
+ const instance = analyticsRef.current;
90
+ if (instance) {
91
+ unregisterAnalyticsInstance(instance);
92
+ }
71
93
  analyticsRef.current = null;
94
+ setStatus("idle");
72
95
  }
73
96
  }
74
97
  })();
@@ -80,20 +103,18 @@ const AnalyticsProvider = (props) => {
80
103
  unregisterAnalyticsInstance(instance);
81
104
  analyticsRef.current = null;
82
105
  }
83
- setIsInitialized(false);
106
+ setStatus("idle");
84
107
  };
85
108
  }, [writeKey, userId, traits, cdnUrl, apiHost]);
86
- const contextValue = useMemo(() => {
87
- if (!analyticsRef.current || !isInitialized) {
88
- return {
89
- isInitialized: false,
90
- track: () => { },
91
- identify: () => { },
92
- page: () => { },
93
- };
109
+ // Calls are delegated as soon as the instance exists, so one made while it loads is buffered and sent
110
+ // once it is ready instead of being dropped. Only `isInitialized` waits for the load. The methods keep
111
+ // their identity from `loading` to `ready`, so an effect that depends on them does not fire twice.
112
+ const isAvailable = status !== "idle";
113
+ const methods = useMemo(() => {
114
+ if (!isAvailable) {
115
+ return { track: () => { }, identify: () => { }, page: () => { } };
94
116
  }
95
117
  return {
96
- isInitialized: true,
97
118
  track: (event, payload) => {
98
119
  analyticsRef.current?.track(event, payload);
99
120
  },
@@ -104,7 +125,8 @@ const AnalyticsProvider = (props) => {
104
125
  analyticsRef.current?.page(name, props);
105
126
  },
106
127
  };
107
- }, [isInitialized]);
128
+ }, [isAvailable]);
129
+ const contextValue = useMemo(() => ({ isInitialized: status === "ready", isAvailable, ...methods }), [status, isAvailable, methods]);
108
130
  return (_jsx(AnalyticsContext.Provider, { value: contextValue, children: children }));
109
131
  };
110
132
  export { AnalyticsContext, AnalyticsProvider };
@@ -1 +1 @@
1
- {"version":3,"file":"AnalyticsProvider.js","sourceRoot":"","sources":["../../../src/contexts/analytics/AnalyticsProvider.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"AnalyticsProvider.js","sourceRoot":"","sources":["../../../src/contexts/analytics/AnalyticsProvider.tsx"],"names":[],"mappings":";AAAA,OAAc,EACZ,aAAa,EACb,SAAS,EACT,OAAO,EACP,MAAM,EACN,QAAQ,GACT,MAAM,OAAO,CAAA;AAMd,OAAO,EAAE,KAAK,EAAE,MAAM,OAAO,CAAA;AAC7B,OAAO,EACL,yBAAyB,EACzB,2BAA2B,GAC5B,MAAM,YAAY,CAAA;AACnB,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAOvD,uEAAuE;AACvE,MAAM,UAAU,GAAG,YAAY,CAAA;AAE/B,MAAM,kBAAkB,GAAG;IACzB,QAAQ,EAAE,UAAU;IACpB,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE;CACnB,CAAA;AAEV,MAAM,gBAAgB,GAAG,aAAa,CAA8B,IAAI,CAAC,CAAA;AAEzE,MAAM,iBAAiB,GAAqC,CAC1D,KAA6B,EAC7B,EAAE;IACF,MAAM,EACJ,QAAQ,EACR,MAAM,EACN,MAAM,EACN,MAAM,EACN,OAAO,EACP,gBAAgB,EAChB,QAAQ,GACT,GAAG,KAAK,CAAA;IACT,MAAM,YAAY,GAAG,MAAM,CAA0B,IAAI,CAAC,CAAA;IAC1D,kGAAkG;IAClG,kEAAkE;IAClE,oGAAoG;IACpG,qCAAqC;IACrC,MAAM,mBAAmB,GAAG,MAAM,CAAC,gBAAgB,CAAC,CAAA;IACpD,SAAS,CAAC,GAAG,EAAE;QACb,mBAAmB,CAAC,OAAO,GAAG,gBAAgB,CAAA;IAChD,CAAC,CAAC,CAAA;IACF,8FAA8F;IAC9F,qGAAqG;IACrG,wDAAwD;IACxD,MAAM,aAAa,GAAG,MAAM,CAAC,CAAC,CAAC,CAAA;IAC/B,sGAAsG;IACtG,iFAAiF;IACjF,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,QAAQ,CAA+B,MAAM,CAAC,CAAA;IAE1E,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,UAAU,GAAG,EAAE,aAAa,CAAC,OAAO,CAAA;QAC1C,MAAM,SAAS,GAAG,GAAG,EAAE,CAAC,UAAU,KAAK,aAAa,CAAC,OAAO,CAAA;QAE5D,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,OAAO,CAAC,GAAG,CAAC,kCAAkC,CAAC,CAAA;QACjD,CAAC;aAAM,IAAI,KAAK,CAAC,SAAS,CAAC,SAAS,CAAC,EAAE,CAAC;YACtC,OAAO,CAAC,GAAG,CAAC,yCAAyC,CAAC,CAAA;QACxD,CAAC;aAAM,CAAC;YACN,KAAK,CAAC,KAAK,IAAI,EAAE;gBACf,IAAI,CAAC;oBACH,gEAAgE;oBAChE,MAAM,EAAE,gBAAgB,EAAE,GAAG,MAAM,MAAM,CAAC,yBAAyB,CAAC,CAAA;oBAEpE,yFAAyF;oBACzF,yFAAyF;oBACzF,wDAAwD;oBACxD,IAAI,CAAC,SAAS,EAAE,EAAE,CAAC;wBACjB,OAAM;oBACR,CAAC;oBAED,MAAM,QAAQ,GAA6B,EAAE,QAAQ,EAAE,CAAA;oBACvD,MAAM,cAAc,GAAG,aAAa,CAAC,MAAM,CAAC,CAAA;oBAC5C,IAAI,cAAc,EAAE,CAAC;wBACnB,QAAQ,CAAC,MAAM,GAAG,cAAc,CAAA;oBAClC,CAAC;oBAED,gGAAgG;oBAChG,+FAA+F;oBAC/F,8DAA8D;oBAC9D,MAAM,eAAe,GAAG,cAAc,CAAC,OAAO,CAAC,CAAA;oBAC/C,MAAM,OAAO,GAAgB;wBAC3B,YAAY,EAAE;4BACZ,CAAC,UAAU,CAAC,EAAE;gCACZ,GAAG,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,eAAe,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gCACxD,gBAAgB,EACd,mBAAmB,CAAC,OAAO,IAAI,kBAAkB;6BACpD;yBACF;qBACF,CAAA;oBAED,MAAM,SAAS,GAAG,gBAAgB,CAAC,IAAI,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAA;oBAE1D,IAAI,MAAM,EAAE,CAAC;wBACX,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;oBACpC,CAAC;oBAED,2FAA2F;oBAC3F,mFAAmF;oBACnF,YAAY,CAAC,OAAO,GAAG,SAAS,CAAA;oBAChC,yBAAyB,CAAC,SAAS,CAAC,CAAA;oBACpC,SAAS,CAAC,SAAS,CAAC,CAAA;oBAEpB,6FAA6F;oBAC7F,8FAA8F;oBAC9F,0FAA0F;oBAC1F,gCAAgC;oBAChC,MAAM,SAAS,CAAA;oBAEf,IAAI,SAAS,EAAE,EAAE,CAAC;wBAChB,SAAS,CAAC,OAAO,CAAC,CAAA;oBACpB,CAAC;gBACH,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBACf,OAAO,CAAC,KAAK,CAAC,mCAAmC,EAAE,KAAK,CAAC,CAAA;oBACzD,IAAI,SAAS,EAAE,EAAE,CAAC;wBAChB,MAAM,QAAQ,GAAG,YAAY,CAAC,OAAO,CAAA;wBACrC,IAAI,QAAQ,EAAE,CAAC;4BACb,2BAA2B,CAAC,QAAQ,CAAC,CAAA;wBACvC,CAAC;wBACD,YAAY,CAAC,OAAO,GAAG,IAAI,CAAA;wBAC3B,SAAS,CAAC,MAAM,CAAC,CAAA;oBACnB,CAAC;gBACH,CAAC;YACH,CAAC,CAAC,EAAE,CAAA;QACN,CAAC;QAED,OAAO,GAAG,EAAE;YACV,aAAa,CAAC,OAAO,EAAE,CAAA;YACvB,MAAM,QAAQ,GAAG,YAAY,CAAC,OAAO,CAAA;YACrC,IAAI,QAAQ,EAAE,CAAC;gBACb,2BAA2B,CAAC,QAAQ,CAAC,CAAA;gBACrC,YAAY,CAAC,OAAO,GAAG,IAAI,CAAA;YAC7B,CAAC;YACD,SAAS,CAAC,MAAM,CAAC,CAAA;QACnB,CAAC,CAAA;IACH,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;IAE/C,sGAAsG;IACtG,uGAAuG;IACvG,mGAAmG;IACnG,MAAM,WAAW,GAAG,MAAM,KAAK,MAAM,CAAA;IACrC,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,EAAE;QAC3B,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,GAAE,CAAC,EAAE,QAAQ,EAAE,GAAG,EAAE,GAAE,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,GAAE,CAAC,EAAE,CAAA;QAChE,CAAC;QAED,OAAO;YACL,KAAK,EAAE,CAAC,KAAa,EAAE,OAAsB,EAAE,EAAE;gBAC/C,YAAY,CAAC,OAAO,EAAE,KAAK,CAAC,KAAK,EAAE,OAAO,CAAC,CAAA;YAC7C,CAAC;YACD,QAAQ,EAAE,CAAC,EAAU,EAAE,MAAgC,EAAE,EAAE;gBACzD,YAAY,CAAC,OAAO,EAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC,CAAA;YAC5C,CAAC;YACD,IAAI,EAAE,CAAC,IAAY,EAAE,KAA+B,EAAE,EAAE;gBACtD,YAAY,CAAC,OAAO,EAAE,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;YACzC,CAAC;SACF,CAAA;IACH,CAAC,EAAE,CAAC,WAAW,CAAC,CAAC,CAAA;IAEjB,MAAM,YAAY,GAAG,OAAO,CAC1B,GAAG,EAAE,CAAC,CAAC,EAAE,aAAa,EAAE,MAAM,KAAK,OAAO,EAAE,WAAW,EAAE,GAAG,OAAO,EAAE,CAAC,EACtE,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,CAAC,CAC/B,CAAA;IAED,OAAO,CACL,KAAC,gBAAgB,CAAC,QAAQ,IAAC,KAAK,EAAE,YAAY,YAC3C,QAAQ,GACiB,CAC7B,CAAA;AACH,CAAC,CAAA;AAED,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,CAAA"}
@@ -1,4 +1,7 @@
1
- import type { EventProperties } from "@segment/analytics-next";
1
+ import type { EventProperties, InitOptions } from "@segment/analytics-next";
2
+ type SegmentIoOptions = Exclude<NonNullable<NonNullable<InitOptions["integrations"]>["Segment.io"]>, boolean>;
3
+ /** How analytics-next's Segment.io integration sends events: standard (one request per event) or batching. */
4
+ type AnalyticsDeliveryStrategy = NonNullable<SegmentIoOptions["deliveryStrategy"]>;
2
5
  type AnalyticsProviderProps = {
3
6
  writeKey: string;
4
7
  userId?: string;
@@ -18,13 +21,34 @@ type AnalyticsProviderProps = {
18
21
  * The provider maps it to the `apiHost` setting of analytics-next's own `Segment.io` integration.
19
22
  */
20
23
  apiHost?: string;
24
+ /**
25
+ * How events are delivered. Defaults to standard delivery with `keepalive`, so an event fired right before a
26
+ * same-tab navigation still goes out. Browsers cap all in-flight keepalive requests of a page at 64KB, so an app
27
+ * that sends large or very frequent events can pass its own strategy, e.g. `{ strategy: "batching" }`.
28
+ *
29
+ * A strategy replaces the default entirely: `{ strategy: "standard", config: { priority: "high" } }` turns
30
+ * keepalive off. Batching already flushes with keepalive when the page unloads, so it normally does not lose an event
31
+ * fired right before a navigation. The unload flush shares the same 64KB cap, though, so a large batch `size` or
32
+ * large payloads can still drop part of it; adding `keepalive: true` to batching makes every batch a keepalive
33
+ * request and brings the 64KB pressure back for the whole session.
34
+ *
35
+ * Read when analytics loads: changing it afterwards does not reload analytics.
36
+ */
37
+ deliveryStrategy?: AnalyticsDeliveryStrategy;
21
38
  children: React.ReactNode;
22
39
  };
23
40
  type TrackPayload = EventProperties;
24
41
  type AnalyticsContextType = {
42
+ /** Segment fetched its settings and registered its plugins, so a new call is dispatched right away. */
25
43
  isInitialized: boolean;
44
+ /**
45
+ * Calls reach Segment: true from the moment the instance exists, when calls made while it still loads are
46
+ * buffered and sent once it is ready. False with no write key, for bots, before the SDK import and after a
47
+ * failed load. Optional so hand-built context values keep compiling; read `isAvailable ?? isInitialized`.
48
+ */
49
+ isAvailable?: boolean;
26
50
  track: (event: string, payload?: TrackPayload) => void;
27
51
  identify: (userId: string, traits?: Record<string, unknown>) => void;
28
52
  page: (name: string, props?: Record<string, unknown>) => void;
29
53
  };
30
- export { type AnalyticsProviderProps, type TrackPayload, type AnalyticsContextType, };
54
+ export { type AnalyticsDeliveryStrategy, type AnalyticsProviderProps, type TrackPayload, type AnalyticsContextType, };
@@ -5,16 +5,11 @@ const useAnalytics = () => {
5
5
  if (!analyticsContext) {
6
6
  throw new Error("useAnalytics must be used within AnalyticsProvider");
7
7
  }
8
- if (!analyticsContext.isInitialized) {
9
- return {
10
- isInitialized: false,
11
- track: () => { },
12
- identify: () => { },
13
- page: () => { },
14
- };
15
- }
8
+ // The provider already hands out no-op methods while there is no instance, and buffering ones while
9
+ // it loads: passing them through keeps a call made during the load from being dropped here.
16
10
  return {
17
- isInitialized: true,
11
+ isInitialized: analyticsContext.isInitialized,
12
+ isAvailable: analyticsContext.isAvailable,
18
13
  track: analyticsContext.track,
19
14
  identify: analyticsContext.identify,
20
15
  page: analyticsContext.page,
@@ -1 +1 @@
1
- {"version":3,"file":"useAnalytics.js","sourceRoot":"","sources":["../../src/hooks/useAnalytics.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,OAAO,CAAA;AAClC,OAAO,EAAE,gBAAgB,EAAE,MAAM,yCAAyC,CAAA;AAG1E,MAAM,YAAY,GAAG,GAAyB,EAAE;IAC9C,MAAM,gBAAgB,GAAG,UAAU,CAAC,gBAAgB,CAAC,CAAA;IACrD,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CAAC,oDAAoD,CAAC,CAAA;IACvE,CAAC;IAED,IAAI,CAAC,gBAAgB,CAAC,aAAa,EAAE,CAAC;QACpC,OAAO;YACL,aAAa,EAAE,KAAK;YACpB,KAAK,EAAE,GAAG,EAAE,GAAE,CAAC;YACf,QAAQ,EAAE,GAAG,EAAE,GAAE,CAAC;YAClB,IAAI,EAAE,GAAG,EAAE,GAAE,CAAC;SACf,CAAA;IACH,CAAC;IAED,OAAO;QACL,aAAa,EAAE,IAAI;QACnB,KAAK,EAAE,gBAAgB,CAAC,KAAK;QAC7B,QAAQ,EAAE,gBAAgB,CAAC,QAAQ;QACnC,IAAI,EAAE,gBAAgB,CAAC,IAAI;KAC5B,CAAA;AACH,CAAC,CAAA;AAED,OAAO,EAAE,YAAY,EAAE,CAAA"}
1
+ {"version":3,"file":"useAnalytics.js","sourceRoot":"","sources":["../../src/hooks/useAnalytics.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,OAAO,CAAA;AAClC,OAAO,EAAE,gBAAgB,EAAE,MAAM,yCAAyC,CAAA;AAG1E,MAAM,YAAY,GAAG,GAAyB,EAAE;IAC9C,MAAM,gBAAgB,GAAG,UAAU,CAAC,gBAAgB,CAAC,CAAA;IACrD,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CAAC,oDAAoD,CAAC,CAAA;IACvE,CAAC;IAED,oGAAoG;IACpG,4FAA4F;IAC5F,OAAO;QACL,aAAa,EAAE,gBAAgB,CAAC,aAAa;QAC7C,WAAW,EAAE,gBAAgB,CAAC,WAAW;QACzC,KAAK,EAAE,gBAAgB,CAAC,KAAK;QAC7B,QAAQ,EAAE,gBAAgB,CAAC,QAAQ;QACnC,IAAI,EAAE,gBAAgB,CAAC,IAAI;KAC5B,CAAA;AACH,CAAC,CAAA;AAED,OAAO,EAAE,YAAY,EAAE,CAAA"}
@@ -8,15 +8,17 @@ type PageTrackingProperties = Record<string, unknown>;
8
8
  * Backwards-compatible with the original signature.
9
9
  *
10
10
  * 2. `usePageTracking(name, properties)` — fires `page(name, properties)` only
11
- * after analytics is initialized AND `name` is a non-empty string. Use this
11
+ * once analytics is available AND `name` is a non-empty string. Use this
12
12
  * shape when the page title is resolved asynchronously (e.g. from a CMS via
13
13
  * Helmet + RTK Query) so the event lands AFTER `document.title` updates,
14
14
  * avoiding the SPA race that lets Segment auto-capture the previous route's
15
15
  * title via `properties.title`.
16
16
  *
17
- * The hook is initialization-aware in both shapes: when `useAnalytics().page`
18
- * is the no-op fallback (Segment not loaded yet), the call is skipped to avoid
19
- * wasted work.
17
+ * Both shapes wait for analytics to be AVAILABLE, not fully initialized: once
18
+ * the instance exists a page view is buffered while Segment loads and sent when
19
+ * it is ready, so a route the visitor leaves in-app before then still counts.
20
+ * Before that (`page` is the no-op fallback) the call is skipped and fires when
21
+ * analytics becomes available.
20
22
  */
21
23
  declare function usePageTracking(path: string): void;
22
24
  declare function usePageTracking(name: string | undefined, properties?: PageTrackingProperties): void;
@@ -1,15 +1,17 @@
1
1
  import { useEffect, useRef } from "react";
2
2
  import { useAnalytics } from "./useAnalytics";
3
3
  function usePageTracking(name, properties) {
4
- const { isInitialized, page } = useAnalytics();
4
+ const { isAvailable: available, isInitialized, page } = useAnalytics();
5
+ // Context values built without `isAvailable` (older providers, test doubles) fall back to readiness.
6
+ const isAvailable = available ?? isInitialized;
5
7
  const propertiesKey = properties ? JSON.stringify(properties) : "";
6
8
  const propertiesRef = useRef(properties);
7
9
  propertiesRef.current = properties;
8
10
  useEffect(() => {
9
- if (!isInitialized || !name)
11
+ if (!isAvailable || !name)
10
12
  return;
11
13
  page(name, propertiesRef.current);
12
- }, [isInitialized, name, propertiesKey, page]);
14
+ }, [isAvailable, name, propertiesKey, page]);
13
15
  }
14
16
  export { usePageTracking };
15
17
  //# sourceMappingURL=usePageTracking.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"usePageTracking.js","sourceRoot":"","sources":["../../src/hooks/usePageTracking.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,OAAO,CAAA;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AA4B7C,SAAS,eAAe,CACtB,IAAwB,EACxB,UAAmC;IAEnC,MAAM,EAAE,aAAa,EAAE,IAAI,EAAE,GAAG,YAAY,EAAE,CAAA;IAC9C,MAAM,aAAa,GAAG,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;IAClE,MAAM,aAAa,GAAG,MAAM,CAAC,UAAU,CAAC,CAAA;IACxC,aAAa,CAAC,OAAO,GAAG,UAAU,CAAA;IAElC,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,aAAa,IAAI,CAAC,IAAI;YAAE,OAAM;QACnC,IAAI,CAAC,IAAI,EAAE,aAAa,CAAC,OAAO,CAAC,CAAA;IACnC,CAAC,EAAE,CAAC,aAAa,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,CAAC,CAAC,CAAA;AAChD,CAAC;AAED,OAAO,EAAE,eAAe,EAAE,CAAA"}
1
+ {"version":3,"file":"usePageTracking.js","sourceRoot":"","sources":["../../src/hooks/usePageTracking.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,OAAO,CAAA;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AA8B7C,SAAS,eAAe,CACtB,IAAwB,EACxB,UAAmC;IAEnC,MAAM,EAAE,WAAW,EAAE,SAAS,EAAE,aAAa,EAAE,IAAI,EAAE,GAAG,YAAY,EAAE,CAAA;IACtE,qGAAqG;IACrG,MAAM,WAAW,GAAG,SAAS,IAAI,aAAa,CAAA;IAC9C,MAAM,aAAa,GAAG,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;IAClE,MAAM,aAAa,GAAG,MAAM,CAAC,UAAU,CAAC,CAAA;IACxC,aAAa,CAAC,OAAO,GAAG,UAAU,CAAA;IAElC,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,WAAW,IAAI,CAAC,IAAI;YAAE,OAAM;QACjC,IAAI,CAAC,IAAI,EAAE,aAAa,CAAC,OAAO,CAAC,CAAA;IACnC,CAAC,EAAE,CAAC,WAAW,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,CAAC,CAAC,CAAA;AAC9C,CAAC;AAED,OAAO,EAAE,eAAe,EAAE,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dcl/hooks",
3
- "version": "2.0.3",
3
+ "version": "2.0.4-20261008193059.commit-428d6ed",
4
4
  "type": "module",
5
5
  "main": "esm/index.js",
6
6
  "module": "esm/index.js",
@@ -80,5 +80,5 @@
80
80
  "<rootDir>/test/setup.ts"
81
81
  ]
82
82
  },
83
- "commit": "7fe4bb37603f3a34f2e53d150a9dc393a24865f9"
83
+ "commit": "428d6ed0266ea35a87301f76446760f3b8837bdc"
84
84
  }