Tic-tac-toe — React SDK @beta-gamer/react
Wrap your game UI in BetaGamerProvider with the session token. The provider connects to the /tictactoe namespace automatically.
Install
npm install @beta-gamer/reactMinimal setup
import { BetaGamerProvider } from '@beta-gamer/react';
import { TictactoeGame } from './TictactoeGame';
export default function Page({ sessionToken }: { sessionToken: string }) {
return (
<BetaGamerProvider
token={sessionToken}
serverUrl="https://api.beta-gamer.com"
>
<TictactoeGame />
</BetaGamerProvider>
);
}Hooks
| Hook | Returns | Description |
|---|---|---|
useSocket() | Socket | null | Raw Socket.IO socket for the /tictactoe namespace. |
useSession() | SessionState | Decoded token payload — players[], game, sessionId. |
useGameState() | GameState | status, winner, reason — updated on game:over. |
Full example
import { useEffect, useRef, useState } from 'react';
import { BetaGamerProvider, useSocket, useSession } from '@beta-gamer/react';
function TictactoeGame() {
const socket = useSocket();
const session = useSession();
const myPlayerId = session.players[0]?.id ?? '';
const roomIdRef = useRef('');
const [board, setBoard] = useState<('X'|'O'|null)[]>(Array(9).fill(null));
const [currentTurn, setCurrentTurn] = useState(0);
const [players, setPlayers] = useState<any[]>([]);
const [myMark, setMyMark] = useState<'X'|'O'>('X');
const [winningLine, setWinningLine] = useState<number[]|null>(null);
const [gameOver, setGameOver] = useState(false);
useEffect(() => {
if (!socket) return;
const join = () => socket.emit('matchmaking:join', {
username: session.players[0]?.displayName,
playerId: myPlayerId,
});
if (socket.connected) join(); else socket.once('connect', join);
socket.on('game:started', ({ roomId, playerId, mark, board: b, players: p, currentTurn: t }) => {
roomIdRef.current = roomId;
setMyMark(mark);
setBoard(b);
setPlayers(p);
setCurrentTurn(t);
});
socket.on('game:move:made', ({ board: b, currentTurn: t, winningLine: wl }) => {
setBoard(b);
setCurrentTurn(t);
if (wl) setWinningLine(wl);
});
socket.on('game:over', ({ winner, reason, winningLine: wl }) => {
if (wl) setWinningLine(wl);
setGameOver(true);
});
return () => {
socket.off('connect', join);
socket.off('game:started');
socket.off('game:move:made');
socket.off('game:over');
};
}, [socket]);
const isMyTurn = players[currentTurn]?.id === myPlayerId;
const move = (position: number) => {
if (!isMyTurn || board[position] || gameOver) return;
socket?.emit('game:move', { roomId: roomIdRef.current, playerId: myPlayerId, position });
};
return (
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 80px)', gap: 8 }}>
{board.map((cell, i) => (
<button key={i} onClick={() => move(i)}
style={{ width: 80, height: 80, fontSize: 32,
background: winningLine?.includes(i) ? '#fef08a' : '#1e293b' }}>
{cell}
</button>
))}
</div>
);
}
export default function Page({ sessionToken }: { sessionToken: string }) {
return (
<BetaGamerProvider token={sessionToken} serverUrl="https://api.beta-gamer.com">
<TictactoeGame />
</BetaGamerProvider>
);
}Beta Gamer GaaS API — questions? support@beta-gamer.com