@djangocfg/centrifugo 1.0.3 → 1.0.4

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.
@@ -2,6 +2,12 @@
2
2
  * useSubscription Hook
3
3
  *
4
4
  * Subscribe to Centrifugo channel with auto-cleanup.
5
+ *
6
+ * Best practices:
7
+ * - Callbacks are stored in refs to avoid re-subscriptions
8
+ * - Proper error handling in callbacks (as recommended by centrifuge-js)
9
+ * - Cleanup on unmount
10
+ * - Stable unsubscribe function
5
11
  */
6
12
 
7
13
  'use client';
@@ -35,21 +41,38 @@ export function useSubscription<T = any>(
35
41
  const [isSubscribed, setIsSubscribed] = useState(false);
36
42
 
37
43
  const unsubscribeRef = useRef<(() => void) | null>(null);
38
- const logger = useRef(createLogger({ source: 'subscription' })).current;
44
+ const logger = useRef(createLogger('useSubscription')).current;
45
+
46
+ // Store callbacks in refs to avoid re-subscriptions when they change
47
+ const onPublicationRef = useRef(onPublication);
48
+ const onErrorRef = useRef(onError);
49
+
50
+ useEffect(() => {
51
+ onPublicationRef.current = onPublication;
52
+ onErrorRef.current = onError;
53
+ }, [onPublication, onError]);
39
54
 
40
55
  // Unsubscribe function
41
56
  const unsubscribe = useCallback(() => {
42
57
  if (unsubscribeRef.current) {
43
- unsubscribeRef.current();
44
- unsubscribeRef.current = null;
45
- setIsSubscribed(false);
46
- logger.info(`Unsubscribed from channel: ${channel}`);
58
+ try {
59
+ unsubscribeRef.current();
60
+ unsubscribeRef.current = null;
61
+ setIsSubscribed(false);
62
+ logger.info(`Unsubscribed from channel: ${channel}`);
63
+ } catch (err) {
64
+ logger.error(`Error during unsubscribe from ${channel}`, err);
65
+ }
47
66
  }
48
67
  }, [channel, logger]);
49
68
 
50
69
  // Subscribe effect
51
70
  useEffect(() => {
52
71
  if (!client || !isConnected || !enabled) {
72
+ // Reset state when not connected
73
+ if (!isConnected && isSubscribed) {
74
+ setIsSubscribed(false);
75
+ }
53
76
  return;
54
77
  }
55
78
 
@@ -57,9 +80,14 @@ export function useSubscription<T = any>(
57
80
 
58
81
  try {
59
82
  const unsub = client.subscribe(channel, (receivedData: T) => {
60
- setData(receivedData);
61
- setError(null);
62
- onPublication?.(receivedData);
83
+ try {
84
+ // Error handling in callback as recommended by centrifuge-js docs
85
+ setData(receivedData);
86
+ setError(null);
87
+ onPublicationRef.current?.(receivedData);
88
+ } catch (callbackError) {
89
+ logger.error(`Error in onPublication callback for ${channel}`, callbackError);
90
+ }
63
91
  });
64
92
 
65
93
  unsubscribeRef.current = unsub;
@@ -68,7 +96,13 @@ export function useSubscription<T = any>(
68
96
  } catch (err) {
69
97
  const subscriptionError = err instanceof Error ? err : new Error('Subscription failed');
70
98
  setError(subscriptionError);
71
- onError?.(subscriptionError);
99
+
100
+ try {
101
+ onErrorRef.current?.(subscriptionError);
102
+ } catch (callbackError) {
103
+ logger.error(`Error in onError callback for ${channel}`, callbackError);
104
+ }
105
+
72
106
  logger.error(`Subscription failed: ${channel}`, subscriptionError);
73
107
  }
74
108
 
@@ -76,7 +110,7 @@ export function useSubscription<T = any>(
76
110
  return () => {
77
111
  unsubscribe();
78
112
  };
79
- }, [client, isConnected, enabled, channel, onPublication, onError, unsubscribe, logger]);
113
+ }, [client, isConnected, enabled, channel, unsubscribe, logger, isSubscribed]);
80
114
 
81
115
  return {
82
116
  data,
package/src/index.ts CHANGED
@@ -100,9 +100,8 @@ export { centrifugoConfig, isDevelopment, isProduction, isStaticBuild } from './
100
100
  export type { CentrifugoConfig } from './config';
101
101
 
102
102
  // ─────────────────────────────────────────────────────────────────────────
103
- // Debug UI (Embedded in Provider, not for direct use)
103
+ // Components (Universal, composable monitoring components)
104
104
  // ─────────────────────────────────────────────────────────────────────────
105
105
 
106
- // Note: DebugPanel is now automatically embedded in CentrifugoProvider
107
- // It shows automatically in development or for admin users
108
- // No need to manually add <DebugPanel /> to your app
106
+ export * from './components';
107
+ export * from './debug';
@@ -7,18 +7,13 @@
7
7
 
8
8
  'use client';
9
9
 
10
- import { createContext, useContext, useState, useEffect, useCallback, useRef, useMemo, lazy, Suspense, type ReactNode } from 'react';
10
+ import { createContext, useContext, useState, useEffect, useCallback, useRef, useMemo, type ReactNode } from 'react';
11
11
  import { useAuth } from '@djangocfg/layouts';
12
12
  import { CentrifugoRPCClient } from '../../core/client';
13
13
  import { createLogger } from '../../core/logger';
14
14
  import type { ConnectionState, CentrifugoToken } from '../../core/types';
15
15
  import { LogsProvider } from '../LogsProvider';
16
- import { isDevelopment, isStaticBuild } from '../../config';
17
-
18
- // Lazy load DebugPanel to reduce bundle size
19
- const DebugPanel = lazy(() =>
20
- import('../../debug/DebugPanel').then((m) => ({ default: m.DebugPanel }))
21
- );
16
+ import { isStaticBuild } from '../../config';
22
17
 
23
18
  // ─────────────────────────────────────────────────────────────────────────
24
19
  // Context
@@ -72,7 +67,7 @@ function CentrifugoProviderInner({
72
67
  url,
73
68
  autoConnect: autoConnectProp = true,
74
69
  }: CentrifugoProviderProps) {
75
- const { isAuthenticated, isLoading, user, isAdminUser } = useAuth();
70
+ const { isAuthenticated, isLoading, user } = useAuth();
76
71
 
77
72
  const [client, setClient] = useState<CentrifugoRPCClient | null>(null);
78
73
  const [isConnected, setIsConnected] = useState(false);
@@ -334,21 +329,9 @@ function CentrifugoProviderInner({
334
329
  enabled,
335
330
  };
336
331
 
337
- // Smart DebugPanel visibility:
338
- // - Show in development mode
339
- // - Show for admin users
340
- const showDebugPanel = useMemo(() => {
341
- return isDevelopment || isAdminUser;
342
- }, [isAdminUser]);
343
-
344
332
  return (
345
333
  <CentrifugoContext.Provider value={value}>
346
334
  {children}
347
- {showDebugPanel && (
348
- <Suspense fallback={null}>
349
- <DebugPanel />
350
- </Suspense>
351
- )}
352
335
  </CentrifugoContext.Provider>
353
336
  );
354
337
  }