Event Listeners

Listen to real-time events in your video calls and react to changes as they happen.

Overview

Event listeners are essential for building reactive, real-time video applications. They allow your application to respond immediately to changes such as participants joining or leaving, media state changes, connection status updates, and user interactions.

BaxCloud provides a comprehensive event system that covers all aspects of a video call, from connection management to participant interactions. By subscribing to these events, you can update your UI in real-time, track analytics, implement custom logic, and provide users with immediate feedback.

Event listeners should always be cleaned up when components unmount to prevent memory leaks and unexpected behavior.

Event Categories

Connection Events

Monitor connection status and network state changes

  • onConnected - Called when successfully connected to room
  • onDisconnected - Called when disconnected from room
  • onReconnecting - Called when attempting to reconnect
  • onReconnected - Called when successfully reconnected

Participant Events

Track participants joining, leaving, and being removed

  • onUserJoined - Called when a participant joins
  • onUserLeft - Called when a participant leaves
  • onParticipantKicked - Called when a participant is removed

Media Events

Listen to audio and video state changes

  • onMediaStarted - Called when media streaming begins
  • onMediaStopped - Called when media streaming stops
  • onCameraToggled - Called when camera is turned on/off
  • onMicrophoneToggled - Called when microphone is muted/unmuted

Messaging Events

Receive chat messages and typing indicators

  • onMessageReceived - Called when a message is received
  • onChatMessage - Called when a chat message arrives
  • onTypingIndicator - Called when someone is typing

Host Events

Monitor host and co-host role changes

  • onHostTransferred - Called when host role is transferred
  • onCoHostPromoted - Called when someone becomes co-host
  • onCoHostDemoted - Called when co-host is demoted

Interaction Events

Track participant interactions like reactions and hand raises

  • onHandRaised - Called when a participant raises hand
  • onHandLowered - Called when hand is lowered
  • onEmojiReaction - Called when someone sends a reaction

Room Management Events

Listen to polls and breakout room activities

  • onPollCreated - Called when a poll is created
  • onPollResponse - Called when someone votes on a poll
  • onBreakoutRoomCreated - Called when breakout room is created

Call Events

Handle call invitations and status changes

  • onInvitationReceived - Called when receiving a call invitation
  • onCallStatusChanged - Called when call status changes

Quality Events

Monitor network quality and performance

  • onNetworkQualityChanged - Called when network quality changes

Basic Usage

Event listeners are registered by calling the corresponding method on your client instance. Each SDK provides a consistent API for subscribing to events.

1import { useEffect } from 'react';
2import { useBaxCloudClient } from '@baxcloud/react';
3
4function MyComponent() {
5  const client = useBaxCloudClient();
6
7  useEffect(() => {
8    // Register event listener
9    const unsubscribe = client.onUserJoined((user) => {
10      console.log('User joined:', user.name);
11    });
12
13    // Cleanup on unmount
14    return () => {
15      unsubscribe();
16    };
17  }, [client]);
18
19  return <div>My Video Call</div>;
20}

Connection Events

Connection events help you track the state of the connection to the room and provide feedback to users about connectivity issues.

1import { useEffect, useState } from 'react';
2import { useBaxCloudClient } from '@baxcloud/react';
3
4function ConnectionStatus() {
5  const client = useBaxCloudClient();
6  const [status, setStatus] = useState('disconnected');
7
8  useEffect(() => {
9    const unsubscribers = [
10      client.onConnected(() => {
11        setStatus('connected');
12        console.log('Successfully connected to room');
13      }),
14      
15      client.onDisconnected(() => {
16        setStatus('disconnected');
17        console.log('Disconnected from room');
18      }),
19      
20      client.onReconnecting(() => {
21        setStatus('reconnecting');
22        console.log('Attempting to reconnect...');
23      }),
24      
25      client.onReconnected(() => {
26        setStatus('connected');
27        console.log('Successfully reconnected');
28      })
29    ];
30
31    return () => {
32      unsubscribers.forEach(unsub => unsub());
33    };
34  }, [client]);
35
36  return (
37    <div className="connection-status">
38      <span className={`status-indicator status-${status}`} />
39      <span>{status}</span>
40    </div>
41  );
42}

Participant Events

Track participants as they join and leave the room to maintain an accurate participant list and update your UI accordingly.

1import { useEffect, useState } from 'react';
2import { useBaxCloudClient } from '@baxcloud/react';
3
4interface Participant {
5  id: string;
6  name: string;
7  isHost: boolean;
8}
9
10function ParticipantList() {
11  const client = useBaxCloudClient();
12  const [participants, setParticipants] = useState<Participant[]>([]);
13
14  useEffect(() => {
15    const unsubscribers = [
16      client.onUserJoined((user) => {
17        setParticipants(prev => [...prev, user]);
18        console.log(`${user.name} joined the room`);
19      }),
20      
21      client.onUserLeft((user) => {
22        setParticipants(prev => prev.filter(p => p.id !== user.id));
23        console.log(`${user.name} left the room`);
24      }),
25      
26      client.onParticipantKicked((user) => {
27        setParticipants(prev => prev.filter(p => p.id !== user.id));
28        console.log(`${user.name} was removed from the room`);
29      })
30    ];
31
32    return () => {
33      unsubscribers.forEach(unsub => unsub());
34    };
35  }, [client]);
36
37  return (
38    <div className="participant-list">
39      <h3>Participants ({participants.length})</h3>
40      <ul>
41        {participants.map(p => (
42          <li key={p.id}>
43            {p.name} {p.isHost && <span>(Host)</span>}
44          </li>
45        ))}
46      </ul>
47    </div>
48  );
49}

Media Events

Monitor when participants start or stop sharing their camera or microphone, and react to media state changes.

1import { useEffect, useState } from 'react';
2import { useBaxCloudClient } from '@baxcloud/react';
3
4function MediaMonitor() {
5  const client = useBaxCloudClient();
6  const [mediaStates, setMediaStates] = useState<Map<string, any>>(new Map());
7
8  useEffect(() => {
9    const unsubscribers = [
10      client.onMediaStarted((data) => {
11        console.log(`${data.userId} started ${data.mediaType}`);
12        setMediaStates(prev => new Map(prev).set(data.userId, {
13          ...prev.get(data.userId),
14          [`${data.mediaType}Active`]: true
15        }));
16      }),
17      
18      client.onMediaStopped((data) => {
19        console.log(`${data.userId} stopped ${data.mediaType}`);
20        setMediaStates(prev => new Map(prev).set(data.userId, {
21          ...prev.get(data.userId),
22          [`${data.mediaType}Active`]: false
23        }));
24      }),
25      
26      client.onCameraToggled((data) => {
27        console.log(`${data.userId} camera: ${data.enabled ? 'on' : 'off'}`);
28      }),
29      
30      client.onMicrophoneToggled((data) => {
31        console.log(`${data.userId} microphone: ${data.enabled ? 'on' : 'off'}`);
32      })
33    ];
34
35    return () => {
36      unsubscribers.forEach(unsub => unsub());
37    };
38  }, [client]);
39
40  return (
41    <div className="media-monitor">
42      {Array.from(mediaStates.entries()).map(([userId, state]) => (
43        <div key={userId} className="media-state">
44          <span>User: {userId}</span>
45          <span>Camera: {state.cameraActive ? '🎥' : '📷'}</span>
46          <span>Mic: {state.microphoneActive ? '🎤' : '🔇'}</span>
47        </div>
48      ))}
49    </div>
50  );
51}

Messaging Events

Handle incoming chat messages and display them in real-time within your application.

1import { useEffect, useState } from 'react';
2import { useBaxCloudClient } from '@baxcloud/react';
3
4interface Message {
5  id: string;
6  userId: string;
7  userName: string;
8  text: string;
9  timestamp: Date;
10}
11
12function ChatMessages() {
13  const client = useBaxCloudClient();
14  const [messages, setMessages] = useState<Message[]>([]);
15  const [typingUsers, setTypingUsers] = useState<Set<string>>(new Set());
16
17  useEffect(() => {
18    const unsubscribers = [
19      client.onMessageReceived((message) => {
20        setMessages(prev => [...prev, message]);
21      }),
22      
23      client.onChatMessage((message) => {
24        console.log(`New message from ${message.userName}: ${message.text}`);
25        setMessages(prev => [...prev, message]);
26      }),
27      
28      client.onTypingIndicator((data) => {
29        setTypingUsers(prev => {
30          const next = new Set(prev);
31          if (data.isTyping) {
32            next.add(data.userId);
33          } else {
34            next.delete(data.userId);
35          }
36          return next;
37        });
38      })
39    ];
40
41    return () => {
42      unsubscribers.forEach(unsub => unsub());
43    };
44  }, [client]);
45
46  return (
47    <div className="chat-container">
48      <div className="messages">
49        {messages.map(msg => (
50          <div key={msg.id} className="message">
51            <strong>{msg.userName}:</strong> {msg.text}
52          </div>
53        ))}
54      </div>
55      {typingUsers.size > 0 && (
56        <div className="typing-indicator">
57          {Array.from(typingUsers).join(', ')} {typingUsers.size === 1 ? 'is' : 'are'} typing...
58        </div>
59      )}
60    </div>
61  );
62}

Poll Events

Track poll creation, responses, and results in real-time. These events help you build interactive Q&A and voting features.

1import { useEffect, useState } from 'react';
2import { useBaxCloudClient } from '@baxcloud/react';
3
4interface Poll {
5  pollId: string;
6  question: string;
7  options: string[];
8  createdBy: string;
9  isActive: boolean;
10  responses: Record<string, string>;
11}
12
13function PollMonitor() {
14  const client = useBaxCloudClient();
15  const [polls, setPolls] = useState<Map<string, Poll>>(new Map());
16  const [pollResults, setPollResults] = useState<Map<string, Record<string, number>>>(new Map());
17
18  useEffect(() => {
19    const unsubscribers = [
20      client.onPollCreated((poll) => {
21        console.log(`New poll created: ${poll.question}`);
22        setPolls(prev => new Map(prev).set(poll.pollId, poll));
23      }),
24      
25      client.onPollResponse((data) => {
26        console.log(`${data.userId} voted for: ${data.selectedOption}`);
27        setPollResults(prev => {
28          const results = new Map(prev);
29          const pollResults = results.get(data.pollId) || {};
30          pollResults[data.selectedOption] = (pollResults[data.selectedOption] || 0) + 1;
31          results.set(data.pollId, pollResults);
32          return results;
33        });
34      }),
35      
36      client.onPollEnded((pollId) => {
37        console.log(`Poll ended: ${pollId}`);
38        setPolls(prev => {
39          const next = new Map(prev);
40          const poll = next.get(pollId);
41          if (poll) {
42            next.set(pollId, { ...poll, isActive: false });
43          }
44          return next;
45        });
46      })
47    ];
48
49    return () => {
50      unsubscribers.forEach(unsub => unsub());
51    };
52  }, [client]);
53
54  return (
55    <div className="poll-monitor">
56      <h3>Active Polls</h3>
57      {Array.from(polls.values()).map(poll => (
58        <div key={poll.pollId} className="poll">
59          <h4>{poll.question}</h4>
60          <div className="poll-results">
61            {poll.options.map(option => (
62              <div key={option}>
63                {option}: {pollResults.get(poll.pollId)?.[option] || 0} votes
64              </div>
65            ))}
66          </div>
67          <span>{poll.isActive ? '🟢 Active' : '🔴 Ended'}</span>
68        </div>
69      ))}
70    </div>
71  );
72}

Breakout Room Events

Monitor breakout room activities including creation, participants joining and leaving. These events are essential for managing collaborative breakout sessions.

1import { useEffect, useState } from 'react';
2import { useBaxCloudClient } from '@baxcloud/react';
3
4interface BreakoutRoom {
5  roomId: string;
6  roomName: string;
7  participants: string[];
8  createdBy: string;
9  createdAt: string;
10}
11
12function BreakoutRoomMonitor() {
13  const client = useBaxCloudClient();
14  const [rooms, setRooms] = useState<Map<string, BreakoutRoom>>(new Map());
15  const [roomActivity, setRoomActivity] = useState<string[]>([]);
16
17  useEffect(() => {
18    const unsubscribers = [
19      client.onBreakoutRoomCreated((room) => {
20        console.log(`Breakout room created: ${room.roomName}`);
21        setRooms(prev => new Map(prev).set(room.roomId, room));
22        setRoomActivity(prev => [...prev, `Room "${room.roomName}" created`]);
23      }),
24      
25      client.onBreakoutRoomJoined((data) => {
26        console.log(`${data.userName} joined room: ${data.roomName}`);
27        setRooms(prev => {
28          const next = new Map(prev);
29          const room = next.get(data.roomId);
30          if (room && !room.participants.includes(data.userId)) {
31            next.set(data.roomId, {
32              ...room,
33              participants: [...room.participants, data.userId],
34            });
35          }
36          return next;
37        });
38        setRoomActivity(prev => [
39          ...prev,
40          `${data.userName} joined "${data.roomName}"`
41        ]);
42      }),
43      
44      client.onBreakoutRoomLeft((data) => {
45        console.log(`${data.userName} left room: ${data.roomName}`);
46        setRooms(prev => {
47          const next = new Map(prev);
48          const room = next.get(data.roomId);
49          if (room) {
50            next.set(data.roomId, {
51              ...room,
52              participants: room.participants.filter(p => p !== data.userId),
53            });
54          }
55          return next;
56        });
57        setRoomActivity(prev => [
58          ...prev,
59          `${data.userName} left "${data.roomName}"`
60        ]);
61      })
62    ];
63
64    return () => {
65      unsubscribers.forEach(unsub => unsub());
66    };
67  }, [client]);
68
69  return (
70    <div className="breakout-monitor">
71      <h3>Breakout Rooms</h3>
72      {Array.from(rooms.values()).map(room => (
73        <div key={room.roomId} className="breakout-room">
74          <h4>{room.roomName}</h4>
75          <p>Participants: {room.participants.length}</p>
76        </div>
77      ))}
78      <div className="activity-log">
79        <h4>Activity Log</h4>
80        {roomActivity.slice(-5).map((activity, i) => (
81          <p key={i}>{activity}</p>
82        ))}
83      </div>
84    </div>
85  );
86}

Host & Permission Events

Track changes to host and co-host roles. These events are critical for implementing permission-based features and UI updates.

1import { useEffect, useState } from 'react';
2import { useBaxCloudClient } from '@baxcloud/react';
3
4interface RoleChange {
5  userId: string;
6  userName: string;
7  role: 'host' | 'co-host' | 'participant';
8  timestamp: Date;
9}
10
11function HostPermissionMonitor() {
12  const client = useBaxCloudClient();
13  const [currentHost, setCurrentHost] = useState<string | null>(null);
14  const [coHosts, setCoHosts] = useState<Set<string>>(new Set());
15  const [roleHistory, setRoleHistory] = useState<RoleChange[]>([]);
16
17  useEffect(() => {
18    const unsubscribers = [
19      client.onHostTransferred((data) => {
20        console.log(`Host transferred from ${data.previousHost} to ${data.newHost}`);
21        setCurrentHost(data.newHost);
22        setRoleHistory(prev => [...prev, {
23          userId: data.newHost,
24          userName: data.newHostName,
25          role: 'host',
26          timestamp: new Date()
27        }]);
28      }),
29      
30      client.onCoHostPromoted((data) => {
31        console.log(`${data.userName} promoted to co-host`);
32        setCoHosts(prev => new Set(prev).add(data.userId));
33        setRoleHistory(prev => [...prev, {
34          userId: data.userId,
35          userName: data.userName,
36          role: 'co-host',
37          timestamp: new Date()
38        }]);
39      }),
40      
41      client.onCoHostDemoted((data) => {
42        console.log(`${data.userName} demoted from co-host`);
43        setCoHosts(prev => {
44          const next = new Set(prev);
45          next.delete(data.userId);
46          return next;
47        });
48        setRoleHistory(prev => [...prev, {
49          userId: data.userId,
50          userName: data.userName,
51          role: 'participant',
52          timestamp: new Date()
53        }]);
54      })
55    ];
56
57    return () => {
58      unsubscribers.forEach(unsub => unsub());
59    };
60  }, [client]);
61
62  return (
63    <div className="host-monitor">
64      <div className="current-roles">
65        <h3>Current Roles</h3>
66        <p>Host: {currentHost || 'None'}</p>
67        <p>Co-Hosts: {coHosts.size}</p>
68      </div>
69      <div className="role-history">
70        <h4>Role Changes</h4>
71        {roleHistory.slice(-5).reverse().map((change, i) => (
72          <div key={i} className="role-change">
73            <span>{change.userName}</span>
74            <span>{change.role}</span>
75            <span>{change.timestamp.toLocaleTimeString()}</span>
76          </div>
77        ))}
78      </div>
79    </div>
80  );
81}

Interaction Events

Monitor participant interactions including hand raises, emoji reactions, pins, and spotlights. These events enhance engagement and communication in video calls.

1import { useEffect, useState } from 'react';
2import { useBaxCloudClient } from '@baxcloud/react';
3
4interface Interaction {
5  userId: string;
6  userName: string;
7  type: 'hand-raised' | 'reaction' | 'pinned' | 'spotlighted';
8  data?: any;
9  timestamp: Date;
10}
11
12function InteractionMonitor() {
13  const client = useBaxCloudClient();
14  const [raisedHands, setRaisedHands] = useState<Set<string>>(new Set());
15  const [recentReactions, setRecentReactions] = useState<Interaction[]>([]);
16  const [pinnedUsers, setPinnedUsers] = useState<Set<string>>(new Set());
17  const [spotlightedUsers, setSpotlightedUsers] = useState<Set<string>>(new Set());
18
19  useEffect(() => {
20    const unsubscribers = [
21      client.onHandRaised((data) => {
22        console.log(`${data.userName} raised their hand`);
23        setRaisedHands(prev => new Set(prev).add(data.userId));
24        setRecentReactions(prev => [...prev, {
25          userId: data.userId,
26          userName: data.userName,
27          type: 'hand-raised',
28          timestamp: new Date()
29        }]);
30      }),
31      
32      client.onHandLowered((data) => {
33        console.log(`${data.userName} lowered their hand`);
34        setRaisedHands(prev => {
35          const next = new Set(prev);
36          next.delete(data.userId);
37          return next;
38        });
39      }),
40      
41      client.onEmojiReaction((data) => {
42        console.log(`${data.userName} sent ${data.emoji}`);
43        setRecentReactions(prev => [...prev, {
44          userId: data.userId,
45          userName: data.userName,
46          type: 'reaction',
47          data: data.emoji,
48          timestamp: new Date()
49        }].slice(-10));
50      }),
51      
52      client.onParticipantPinned((data) => {
53        console.log(`${data.userName} was pinned`);
54        setPinnedUsers(prev => new Set(prev).add(data.userId));
55      }),
56      
57      client.onParticipantSpotlighted((data) => {
58        console.log(`${data.userName} was spotlighted`);
59        setSpotlightedUsers(prev => new Set(prev).add(data.userId));
60      })
61    ];
62
63    return () => {
64      unsubscribers.forEach(unsub => unsub());
65    };
66  }, [client]);
67
68  return (
69    <div className="interaction-monitor">
70      <div className="raised-hands">
71        <h3>Raised Hands ({raisedHands.size})</h3>
72      </div>
73      <div className="reactions">
74        <h3>Recent Reactions</h3>
75        {recentReactions.map((reaction, i) => (
76          <div key={i}>
77            {reaction.userName}: {reaction.data || '✋'}
78          </div>
79        ))}
80      </div>
81      <div className="pinned-users">
82        <h3>Pinned: {pinnedUsers.size}</h3>
83      </div>
84      <div className="spotlighted-users">
85        <h3>Spotlighted: {spotlightedUsers.size}</h3>
86      </div>
87    </div>
88  );
89}

Waiting Room Events

Monitor waiting room activity to control who can join your calls. These events are essential for implementing admission control and security features.

1import { useEffect, useState } from 'react';
2import { useBaxCloudClient } from '@baxcloud/react';
3
4interface WaitingParticipant {
5  userId: string;
6  userName: string;
7  joinedAt: Date;
8  status: 'waiting' | 'approved' | 'rejected';
9}
10
11function WaitingRoomMonitor() {
12  const client = useBaxCloudClient();
13  const [waitingParticipants, setWaitingParticipants] = useState<Map<string, WaitingParticipant>>(new Map());
14  const [admissionHistory, setAdmissionHistory] = useState<any[]>([]);
15
16  useEffect(() => {
17    const unsubscribers = [
18      client.onWaitingRoomParticipantJoined((data) => {
19        console.log(`${data.userName} joined waiting room`);
20        setWaitingParticipants(prev => new Map(prev).set(data.userId, {
21          userId: data.userId,
22          userName: data.userName,
23          joinedAt: new Date(),
24          status: 'waiting'
25        }));
26      }),
27      
28      client.onWaitingRoomParticipantApproved((data) => {
29        console.log(`${data.userName} was approved`);
30        setWaitingParticipants(prev => {
31          const next = new Map(prev);
32          next.delete(data.userId);
33          return next;
34        });
35        setAdmissionHistory(prev => [...prev, {
36          userId: data.userId,
37          userName: data.userName,
38          status: 'approved',
39          timestamp: new Date()
40        }]);
41      }),
42      
43      client.onWaitingRoomParticipantRejected((data) => {
44        console.log(`${data.userName} was rejected`);
45        setWaitingParticipants(prev => {
46          const next = new Map(prev);
47          next.delete(data.userId);
48          return next;
49        });
50        setAdmissionHistory(prev => [...prev, {
51          userId: data.userId,
52          userName: data.userName,
53          status: 'rejected',
54          timestamp: new Date()
55        }]);
56      })
57    ];
58
59    return () => {
60      unsubscribers.forEach(unsub => unsub());
61    };
62  }, [client]);
63
64  return (
65    <div className="waiting-room-monitor">
66      <div className="waiting-participants">
67        <h3>Waiting Room ({waitingParticipants.size})</h3>
68        {Array.from(waitingParticipants.values()).map(participant => (
69          <div key={participant.userId} className="waiting-participant">
70            <span>{participant.userName}</span>
71            <button onClick={() => client.approveWaitingParticipant(participant.userId)}>
72              Approve
73            </button>
74            <button onClick={() => client.rejectWaitingParticipant(participant.userId)}>
75              Reject
76            </button>
77          </div>
78        ))}
79      </div>
80      <div className="admission-history">
81        <h4>Recent Decisions</h4>
82        {admissionHistory.slice(-5).reverse().map((entry, i) => (
83          <div key={i}>
84            {entry.userName}: {entry.status}
85          </div>
86        ))}
87      </div>
88    </div>
89  );
90}

Network Quality Events

Monitor network quality changes for participants to provide feedback about connection issues and adjust video quality dynamically.

1import { useEffect, useState } from 'react';
2import { useBaxCloudClient } from '@baxcloud/react';
3
4type NetworkQuality = 'excellent' | 'good' | 'poor' | 'disconnected';
5
6interface ParticipantQuality {
7  userId: string;
8  userName: string;
9  quality: NetworkQuality;
10  lastUpdated: Date;
11}
12
13function NetworkQualityMonitor() {
14  const client = useBaxCloudClient();
15  const [qualityMap, setQualityMap] = useState<Map<string, ParticipantQuality>>(new Map());
16  const [poorQualityCount, setPoorQualityCount] = useState(0);
17
18  useEffect(() => {
19    const unsubscribe = client.onNetworkQualityChanged((data) => {
20      console.log(`${data.userName} network quality: ${data.quality}`);
21      
22      setQualityMap(prev => {
23        const next = new Map(prev);
24        next.set(data.userId, {
25          userId: data.userId,
26          userName: data.userName,
27          quality: data.quality,
28          lastUpdated: new Date()
29        });
30        return next;
31      });
32
33      // Count participants with poor connection
34      const poorCount = Array.from(qualityMap.values())
35        .filter(p => p.quality === 'poor' || p.quality === 'disconnected')
36        .length;
37      setPoorQualityCount(poorCount);
38    });
39
40    return () => {
41      unsubscribe();
42    };
43  }, [client, qualityMap]);
44
45  const getQualityIcon = (quality: NetworkQuality) => {
46    switch (quality) {
47      case 'excellent': return '🟢';
48      case 'good': return '🟡';
49      case 'poor': return '🟠';
50      case 'disconnected': return '🔴';
51    }
52  };
53
54  return (
55    <div className="network-monitor">
56      <h3>Network Quality</h3>
57      {poorQualityCount > 0 && (
58        <div className="warning">
59          ⚠️ {poorQualityCount} participant(s) experiencing connection issues
60        </div>
61      )}
62      <div className="quality-list">
63        {Array.from(qualityMap.values()).map(participant => (
64          <div key={participant.userId} className="participant-quality">
65            <span>{getQualityIcon(participant.quality)}</span>
66            <span>{participant.userName}</span>
67            <span className="quality-label">{participant.quality}</span>
68          </div>
69        ))}
70      </div>
71    </div>
72  );
73}

Call & Invitation Events

Handle incoming call invitations and track call status changes. These events enable building call notification systems and managing call lifecycle.

1import { useEffect, useState } from 'react';
2import { useBaxCloudClient } from '@baxcloud/react';
3
4interface CallInvitation {
5  invitationId: string;
6  roomName: string;
7  from: string;
8  fromName: string;
9  receivedAt: Date;
10}
11
12type CallStatus = 'idle' | 'ringing' | 'connecting' | 'connected' | 'ended';
13
14function CallInvitationMonitor() {
15  const client = useBaxCloudClient();
16  const [pendingInvitations, setPendingInvitations] = useState<CallInvitation[]>([]);
17  const [callStatus, setCallStatus] = useState<CallStatus>('idle');
18  const [callHistory, setCallHistory] = useState<any[]>([]);
19
20  useEffect(() => {
21    const unsubscribers = [
22      client.onInvitationReceived((invitation) => {
23        console.log(`Incoming call from ${invitation.fromName}`);
24        setPendingInvitations(prev => [...prev, {
25          invitationId: invitation.invitationId,
26          roomName: invitation.roomName,
27          from: invitation.from,
28          fromName: invitation.fromName,
29          receivedAt: new Date()
30        }]);
31        
32        // Play notification sound
33        playNotificationSound();
34      }),
35      
36      client.onCallStatusChanged((data) => {
37        console.log(`Call status changed: ${data.status}`);
38        setCallStatus(data.status);
39        
40        if (data.status === 'ended') {
41          setCallHistory(prev => [...prev, {
42            roomName: data.roomName,
43            duration: data.duration,
44            endedAt: new Date()
45          }]);
46        }
47      })
48    ];
49
50    return () => {
51      unsubscribers.forEach(unsub => unsub());
52    };
53  }, [client]);
54
55  const handleAccept = (invitation: CallInvitation) => {
56    client.acceptInvitation(invitation.invitationId);
57    setPendingInvitations(prev => 
58      prev.filter(inv => inv.invitationId !== invitation.invitationId)
59    );
60  };
61
62  const handleReject = (invitation: CallInvitation) => {
63    client.rejectInvitation(invitation.invitationId);
64    setPendingInvitations(prev => 
65      prev.filter(inv => inv.invitationId !== invitation.invitationId)
66    );
67  };
68
69  const playNotificationSound = () => {
70    // Play notification sound
71    const audio = new Audio('/notification.mp3');
72    audio.play();
73  };
74
75  return (
76    <div className="call-monitor">
77      {pendingInvitations.length > 0 && (
78        <div className="incoming-calls">
79          <h3>Incoming Calls</h3>
80          {pendingInvitations.map(invitation => (
81            <div key={invitation.invitationId} className="invitation-card">
82              <h4>{invitation.fromName} is calling...</h4>
83              <p>Room: {invitation.roomName}</p>
84              <div className="actions">
85                <button onClick={() => handleAccept(invitation)} className="accept">
86                  Accept
87                </button>
88                <button onClick={() => handleReject(invitation)} className="reject">
89                  Decline
90                </button>
91              </div>
92            </div>
93          ))}
94        </div>
95      )}
96      
97      <div className="call-status">
98        <h3>Call Status: {callStatus}</h3>
99      </div>
100      
101      <div className="call-history">
102        <h4>Recent Calls</h4>
103        {callHistory.slice(-5).reverse().map((call, i) => (
104          <div key={i}>
105            {call.roomName} - {call.duration}s
106          </div>
107        ))}
108      </div>
109    </div>
110  );
111}

Unsubscribing from Events

It's crucial to unsubscribe from event listeners when they're no longer needed to prevent memory leaks and unexpected behavior. All event listener methods return an unsubscribe function.

Failing to unsubscribe from events can lead to memory leaks, especially in React components that mount and unmount frequently. Always clean up event listeners in useEffect cleanup functions or component lifecycle methods.
1import { useEffect } from 'react';
2import { useBaxCloudClient } from '@baxcloud/react';
3
4function MyComponent() {
5  const client = useBaxCloudClient();
6
7  useEffect(() => {
8    // Single event listener
9    const unsubscribe = client.onUserJoined((user) => {
10      console.log('User joined:', user.name);
11    });
12
13    // Cleanup on unmount
14    return () => {
15      unsubscribe();
16    };
17  }, [client]);
18
19  useEffect(() => {
20    // Multiple event listeners
21    const unsubscribers = [
22      client.onUserJoined((user) => console.log('Joined:', user.name)),
23      client.onUserLeft((user) => console.log('Left:', user.name)),
24      client.onMessageReceived((msg) => console.log('Message:', msg))
25    ];
26
27    // Cleanup all listeners
28    return () => {
29      unsubscribers.forEach(unsub => unsub());
30    };
31  }, [client]);
32
33  return <div>My Component</div>;
34}

Best Practices

Always Unsubscribe

Prevent memory leaks by cleaning up event listeners

Always unsubscribe from event listeners when your component unmounts or when the listener is no longer needed. In React, use the cleanup function of useEffect. In Flutter, use dispose. In Swift, use deinit. In Kotlin, use onDestroy or onCleared.

Keep Event Handlers Lightweight

Avoid heavy operations in event callbacks

Event handlers should execute quickly. Avoid performing heavy computations, expensive API calls, or complex state updates directly in event callbacks. Instead, trigger these operations asynchronously or queue them for processing.

Use Debouncing for High-Frequency Events

Prevent performance issues from rapid event firing

Some events like onNetworkQualityChanged or onTypingIndicator can fire frequently. Use debouncing or throttling techniques to limit how often your handler executes, especially if it triggers UI updates or state changes.

Handle Errors Gracefully

Implement error handling in event callbacks

Wrap your event handler logic in try-catch blocks to prevent errors from breaking your application. Log errors for debugging but ensure the app continues to function even if an event handler fails.

Batch State Updates

Optimize React re-renders by batching updates

When handling multiple related events that affect state, consider batching state updates together. In React 18+, automatic batching helps, but you can use unstable_batchedUpdates for older versions or when updating from async callbacks.

Test Event Flows

Verify event handling works correctly

Test common event scenarios like participants joining/leaving, connection interruptions, and media state changes. Ensure your UI responds correctly and that cleanup happens properly.

Pro Tip: Use a custom hook in React to encapsulate common event listener patterns. This makes it easier to reuse event handling logic across components and ensures consistent cleanup.

1// Custom hook for participant tracking
2function useParticipants() {
3  const client = useBaxCloudClient();
4  const [participants, setParticipants] = useState([]);
5
6  useEffect(() => {
7    const unsubscribers = [
8      client.onUserJoined((user) => 
9        setParticipants(prev => [...prev, user])
10      ),
11      client.onUserLeft((user) => 
12        setParticipants(prev => prev.filter(p => p.id !== user.id))
13      )
14    ];
15
16    return () => unsubscribers.forEach(unsub => unsub());
17  }, [client]);
18
19  return participants;
20}