EventBus & hooks — React Native
React to game events without touching the socket directly. The EventBus is a typed singleton fed by all game hooks and board components automatically.
EventBus
Works anywhere in your app — inside or outside React components — as long as a game hook or board component is mounted and feeding events into it.
import { EventBus } from '@beta-gamer/react-native';
// Subscribe — returns an unsubscribe function
const unsub = EventBus.on('game:over', ({ winner, reason }) => {
analytics.track('game_ended', { winner, reason });
});
unsub(); // remove listener
// Fire once then auto-remove
EventBus.once('game:started', ({ players }) => {
console.log('game started with', players);
});useEventBus()
Hook wrapper around EventBus. The on() it returns auto-cleans up when the component unmounts — no manual unsubscribe needed.
import { useEffect } from 'react';
import { useEventBus } from '@beta-gamer/react-native';
function GameOverlay() {
const { on } = useEventBus();
useEffect(() => {
const unsub1 = on('game:started', ({ players }) => setPlayers(players));
const unsub2 = on('game:over', ({ winner }) => showResult(winner));
return () => { unsub1(); unsub2(); };
}, []);
return null;
}All events
| Event | Payload | Games |
|---|---|---|
| game:started | { roomId, playerId, players, currentTurn, ...gameSpecific } | all |
| game:over | { winner: string | null, reason: string } | all |
| game:move:made | { board, currentTurn, ...gameSpecific } | all |
| afk:status | { playerId, expiresAt } | null | all |
| connection_error | { message: string } | all |
| timer:update | { playerTimers: Record<string, number> } | chess |
| chess:afk_warning | { playerId, secondsRemaining } | chess |
| chess:afk_warning_cleared | void | chess |
| game:valid_moves | { position: number, moves: Move[] } | checkers |
| checkers:afk_warning | { playerId, secondsRemaining } | checkers |
| checkers:afk_warning_cleared | void | checkers |
| connect4:afk_warning | { playerId, secondsRemaining } | connect4 |
| connect4:afk_warning_cleared | void | connect4 |
| tictactoe:afk_warning | { playerId, secondsRemaining } | tictactoe |
| tictactoe:afk_warning_cleared | void | tictactoe |
BetaGamerEvents. Import the type if needed: import type { BetaGamerEvents } from '@beta-gamer/react-native'Other hooks
useSocket()
Returns the active socket from provider context — set by the provider (connectSocket=true) or registered by a game hook. May be null briefly on first render. Use when you need to emit custom events.
import { useSocket } from '@beta-gamer/react-native';
function ResignButton({ roomId, playerId }) {
const socket = useSocket();
return (
<TouchableOpacity onPress={() => socket?.emit('game:resign', { roomId, playerId })}>
<Text>Resign</Text>
</TouchableOpacity>
);
}useConnectionError()
Returns the latest connection error as a string, or null. No built-in alert is shown — handle it yourself.
import { useConnectionError } from '@beta-gamer/react-native';
function GameUI() {
const error = useConnectionError();
if (error) return <Text style={{ color: 'red' }}>{error}</Text>;
return <ChessBoard layout="default" onLeave={handleLeave} />;
}useGameState()
Returns the provider-level game state — updated by game hooks via _updateGameState when game:over fires.
import { useGameState } from '@beta-gamer/react-native';
const { status, winner, reason } = useGameState();
// status: 'pending' | 'active' | 'ended'useSession()
Returns the decoded JWT payload — game type, match type, players, theme, etc.
import { useSession } from '@beta-gamer/react-native';
const { game, matchType, players, theme, sessionId } = useSession();