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 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.
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}