🎮 Beta Gamer

Game hooks — React Native

Each board component is powered by a game hook internally. You can call the hook directly to build a fully custom board UI, or pass it to a board component to share the socket.

Available hooks

HookGame
useChessGame()Chess
useCheckersGame()Checkers
useConnect4Game()Connect 4
useTictactoeGame()Tic-tac-toe

Each hook connects to the game server, handles matchmaking, and returns all the state you need to render a board. They also feed events into the EventBus automatically.

Three ways to use them

1. Just the board — no hook needed

The board calls the hook internally. Simplest option.

<ChessBoard layout="default" onLeave={handleLeave} />

2. Hook + board — shared socket, access to state

Call the hook yourself, pass it to the board. One socket, full state access.

const game = useChessGame();

// Read anything from the hook
console.log(game.isMyTurn, game.players, game.fen, game.clocks);

// Board uses your hook instance — no duplicate socket
<ChessBoard game={game} layout="board-only" onLeave={handleLeave} />

3. Hook only — fully custom board UI

Skip the board component entirely. Build your own UI with the hook's state and handlers.

import { BetaGamerProvider, useChessGame } from '@beta-gamer/react-native';
import { View, TouchableOpacity, Text } from 'react-native';

function MyCustomChessBoard() {
  const {
    chess, fen, selected, legalMoves, lastMove,
    isMyTurn, myColor, players, clocks,
    handleSquarePress, gameOver, gameResult,
  } = useChessGame();

  const files = ['a','b','c','d','e','f','g','h'];
  const ranks = [8,7,6,5,4,3,2,1];

  return (
    <View>
      {ranks.map(rank => (
        <View key={rank} style={{ flexDirection: 'row' }}>
          {files.map(file => {
            const square = `${file}${rank}`;
            const piece  = chess.get(square);
            const isSelected  = selected === square;
            const isLegal     = legalMoves.includes(square);
            const isLastMove  = lastMove?.from === square || lastMove?.to === square;
            return (
              <TouchableOpacity
                key={square}
                onPress={() => handleSquarePress(square)}
                style={{
                  width: 44, height: 44,
                  backgroundColor: isSelected ? '#7C3AED'
                    : isLegal     ? '#4ADE80'
                    : isLastMove  ? '#FCD34D'
                    : (file.charCodeAt(0) + rank) % 2 === 0 ? '#B58863' : '#F0D9B5',
                  alignItems: 'center', justifyContent: 'center',
                }}
              >
                {piece && <Text style={{ fontSize: 28 }}>{pieceEmoji(piece)}</Text>}
              </TouchableOpacity>
            );
          })}
        </View>
      ))}
      <Text style={{ color: '#9CA3AF', marginTop: 8 }}>
        {gameOver ? (gameResult?.winner ? 'Game over' : 'Draw') : isMyTurn ? 'Your turn' : "Opponent's turn"}
      </Text>
    </View>
  );
}

function pieceEmoji({ type, color }: { type: string; color: string }) {
  const map: Record<string, string> = { p: color === 'w' ? '♙' : '♟', r: color === 'w' ? '♖' : '♜', n: color === 'w' ? '♘' : '♞', b: color === 'w' ? '♗' : '♝', q: color === 'w' ? '♕' : '♛', k: color === 'w' ? '♔' : '♚' };
  return map[type] ?? '';
}

export function GameScreen({ sessionToken }) {
  return (
    <BetaGamerProvider token={sessionToken} connectSocket={false}>
      <MyCustomChessBoard />
    </BetaGamerProvider>
  );
}

useChessGame() return value

FieldTypeDescription
socketSocket | nullThe active socket.io socket
roomIdstring | nullCurrent room ID (set after game:started)
myPlayerIdstringYour player ID assigned by the server
myColor"white" | "black"Your assigned color
fenstringCurrent board position in FEN notation
chessChesschess.js instance — use for move validation, piece lookup, etc.
currentTurnnumberIndex into players[] of whose turn it is
playersany[]Both players (set after game:started)
clocks{ self: number; opponent: number }Remaining time in seconds
selectedstring | nullCurrently selected square (e.g. "e2")
legalMovesstring[]Legal destination squares for selected piece
lastMove{ from: string; to: string } | nullLast move made
moveHistory{ san: string }[]Full move history in algebraic notation
promotionMove{ from: string; to: string } | nullSet when a pawn promotion is pending
isMyTurnbooleanWhether it is your turn
gameOverbooleanTrue after game ends (with 400ms delay)
gameResult{ winner: string | null; reason: string } | nullSet when game ends
afkWarning{ playerId: string; secondsRemaining: number } | nullAFK countdown state
handleSquarePress(square: string) => voidCall on square tap — handles selection and move emission
emitMove(from, to, promotion?) => voidEmit a move directly (used for promotion)
setPromotionMove(m | null) => voidClear or set the pending promotion move
Note: useCheckersGame, useConnect4Game, and useTictactoeGame follow the same pattern with game-specific fields (e.g. board, validMoves, winningLine). See the game-specific docs in the sidebar for their full return values.
Beta Gamer GaaS API — questions? support@beta-gamer.com