Hooks & State Management
useTournament Hook
The useTournament hook is the primary interface for retrieving tournament context within the application. It abstracts the complexity of resolving tournament identities, whether provided via a unique UUID or a custom URL slug.
Usage
This hook is typically used in any page under the /t/[id] route to ensure the application has a valid tournament instance before rendering UI components.
import { useTournament } from "@/hooks/use-tournament";
export default function TournamentComponent({ params }) {
const { id: paramId } = use(params);
const { tournament, tournamentId, loading, error } = useTournament(paramId);
if (loading) return <Loader />;
if (error) return <ErrorState message={error} />;
return <h1>{tournament.name}</h1>;
}
Return Values
| Property | Type | Description |
| :--- | :--- | :--- |
| tournament | Object | The full tournament record from the database (includes name, status, cut_size, etc.). |
| tournamentId | String | The resolved UUID of the tournament. Useful for secondary queries when the initial paramId is a slug. |
| loading | Boolean | Becomes false once the tournament data has been successfully resolved. |
| error | String | Contains error details if the tournament does not exist or the fetch fails. |
Local State Synchronization
While useTournament provides the high-level context, specific views (like the Bracket or Admin Console) manage their own internal state for matches and participants to ensure responsive UI updates.
Pattern: Refresh Triggers
For pages requiring manual data re-validation (e.g., after reporting a match score), the application utilizes a refreshTrigger pattern. This forces a local useEffect to re-run and pull the latest data from Supabase without a full page reload.
const [refreshTrigger, setRefreshTrigger] = useState(0);
useEffect(() => {
if (tournamentId) fetchData();
}, [tournamentId, refreshTrigger]);
// Call this after a successful Server Action
const handleUpdate = () => setRefreshTrigger(prev => prev + 1);
Pattern: Participant Mapping
To optimize performance in the Bracket and Standings views, participant data is often fetched once and converted into a Map (Key-Value pair) where the key is the participant_id. This allows for O(1) lookups of names and scores when rendering match cards.
const [participants, setParticipants] = useState({});
// Inside fetchData:
const { data } = await supabase.from("participants").select("*").eq("tournament_id", id);
const pMap = {};
data.forEach(p => pMap[p.id] = p);
setParticipants(pMap);
Form & Modal State
The BBX platform utilizes local state to manage complex interactions like match scoring and tournament creation.
Match Scoring State
The MatchScoringModal component manages its own internal form state for scores (e.g., scoreA, scoreB) and victory types (e.g., "Burst Finish", "Extreme Finish"). This state is ephemeral and is only persisted to the database upon calling the reportMatchAction server action.
Loading States
All primary action buttons (Launch Tournament, Advance Round, etc.) are tied to local loading or submitting boolean states. This prevents double-submission and provides immediate visual feedback to the tournament organizer.
const [submitting, setSubmitting] = useState(false);
async function handleSubmit(formData) {
setSubmitting(true);
const res = await reportMatchAction(formData);
if (!res.success) setSubmitting(false); // Reset on error to allow retry
}