usegamigameapi
v1.0.15
Published
A React hook for managing interactive quiz/game flows through iframe communication using postMessage. This hook handles question flow, answer submission, result tracking, and seamless communication between a parent window and an iframe.
Downloads
62
Readme
useGameAPI
A React hook for managing interactive quiz/game flows through iframe communication using postMessage. This hook handles question flow, answer submission, result tracking, and seamless communication between a parent window and an iframe.
Installation
npm install usegamigameapiRequirements
- React >= 18
Basic Usage
import { useGameAPI } from "usegamigameapi";
function QuizComponent() {
const {
quiz,
username,
selectedAnswer,
handleAnswerSelect,
updateAnswer,
handleContinue,
isCompleted,
currentResult,
} = useGameAPI({
onAnswerCorrect: ({ currentQuestionIndex }) => {
console.log("Correct answer at question", currentQuestionIndex);
// Trigger confetti or other celebrations
},
onAnswerIncorrect: () => {
console.log("Incorrect answer");
},
});
// Render your quiz UI using the returned state and methods
}API Documentation
Hook Parameters
The useGameAPI hook accepts an object with the following optional callbacks:
| Parameter | Type | Description |
| ------------------- | ---------------------------------------------------------------------------------------------------------- | -------------------------------------------------------- |
| onAnswerCorrect | (params: { currentQuestionIndex: number; correctAnswerId?: number; incorrectAnswerId?: number }) => void | Callback fired when the user submits a correct answer |
| onAnswerIncorrect | (params: { currentQuestionIndex: number; correctAnswerId?: number; incorrectAnswerId?: number }) => void | Callback fired when the user submits an incorrect answer |
Return Values
The hook returns an object with the following properties:
State Data
| Property | Type | Description |
| ---------------------- | --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| quiz | any | The current question data received from the parent window (contains text, audio URL, answers array, etc.) |
| currentResult | any | The result object after submitting an answer (contains isCorrect, isLastQuestion, correctAnswerId, incorrectAnswerId, and explanation) |
| answers | (boolean \| null)[] | Array of answer history where each index corresponds to a question (true = correct, false = incorrect, null = unanswered) |
| correctCount | number | Total count of correctly answered questions |
| currentQuestionIndex | number | Zero-based index of the current question |
| username | string | The username received from the parent window during initialization |
State Status
| Property | Type | Description |
| ---------------- | ----------------------------------------- | ------------------------------------------------------------------------------------------------ |
| selectedAnswer | { id: number; content: string } \| null | The currently selected answer object |
| isSubmitting | boolean | Indicates whether an answer submission is in progress |
| hasSubmitted | boolean | Indicates whether the current question has been submitted and is waiting for the Continue action |
| isCompleted | boolean | Indicates whether all questions have been completed |
Methods
| Method | Type | Description |
| -------------------- | --------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| handleAnswerSelect | (answer: { id: number; content: string }) => void | Selects an answer for the current question. Only works if not submitting and not already submitted |
| updateAnswer | () => Promise<void> | Submits the selected answer to the parent window. Returns a promise that resolves immediately |
| handleContinue | () => void | Moves to the next question or marks the quiz as completed if it's the last question. Only works after an answer has been submitted |
| finish | () => void | Sends a finish signal to the parent window (typically used to resize iframe) |
Usage Examples
Complete Quiz Flow Example
import { useGameAPI } from "usegamigameapi";
function GameQuiz() {
const {
quiz,
username,
selectedAnswer,
currentResult,
answers,
correctCount,
currentQuestionIndex,
isSubmitting,
hasSubmitted,
isCompleted,
handleAnswerSelect,
updateAnswer,
handleContinue,
finish,
} = useGameAPI({
onAnswerCorrect: ({ currentQuestionIndex, correctAnswerId }) => {
// Show success animation or confetti
console.log(`Question ${currentQuestionIndex + 1} answered correctly!`, {
correctAnswerId,
});
},
onAnswerIncorrect: ({
currentQuestionIndex,
correctAnswerId,
incorrectAnswerId,
}) => {
// Show error feedback
console.log("Incorrect answer", { correctAnswerId, incorrectAnswerId });
},
});
// Show loading state while waiting for first question
if (!quiz) {
return <div>Loading quiz...</div>;
}
// Show completion screen
if (isCompleted) {
return (
<div>
<h2>Quiz Completed!</h2>
<p>
You got {correctCount} out of {answers.length} questions correct.
</p>
<button onClick={finish}>Finish</button>
</div>
);
}
return (
<div>
{/* Progress bar */}
<div>
Question {currentQuestionIndex + 1} of {answers.length + 1}
<div style={{ display: "flex", gap: "4px" }}>
{answers.map((answer, idx) => (
<div
key={idx}
style={{
width: "20px",
height: "4px",
backgroundColor:
answer === true ? "green" : answer === false ? "red" : "gray",
}}
/>
))}
</div>
</div>
{/* Question */}
<h3>{quiz.text}</h3>
{quiz.audioUrl && <audio src={quiz.audioUrl} controls />}
{/* Answer options */}
<div>
{quiz.answers?.map((answer: { id: number; content: string }) => (
<button
key={answer.id}
onClick={() => handleAnswerSelect(answer)}
disabled={isSubmitting || hasSubmitted}
style={{
backgroundColor:
selectedAnswer?.id === answer.id ? "#007bff" : "#f0f0f0",
opacity: hasSubmitted ? 0.6 : 1,
}}>
{answer.content}
</button>
))}
</div>
{/* Submit button */}
{!hasSubmitted && (
<button
onClick={updateAnswer}
disabled={!selectedAnswer || isSubmitting}>
Submit Answer
</button>
)}
{/* Result display and Continue button */}
{hasSubmitted && currentResult && (
<div>
<p>{currentResult.isCorrect ? "✓ Correct!" : "✗ Incorrect"}</p>
{currentResult.explanation && <p>{currentResult.explanation}</p>}
<button onClick={handleContinue}>
{currentResult.isLastQuestion ? "Finish Quiz" : "Next Question"}
</button>
</div>
)}
{/* Score */}
<div>Score: {correctCount}</div>
</div>
);
}Minimal Example
import { useGameAPI } from "usegamigameapi";
function SimpleQuiz() {
const {
quiz,
handleAnswerSelect,
updateAnswer,
handleContinue,
selectedAnswer,
hasSubmitted,
} = useGameAPI();
return (
<div>
<h2>{quiz?.text}</h2>
{quiz?.answers?.map((answer) => (
<button
key={answer.id}
onClick={() => handleAnswerSelect(answer)}
disabled={hasSubmitted}>
{answer.content}
</button>
))}
{!hasSubmitted && (
<button onClick={updateAnswer} disabled={!selectedAnswer}>
Submit
</button>
)}
{hasSubmitted && <button onClick={handleContinue}>Continue</button>}
</div>
);
}Architecture
This hook uses a bridge pattern to facilitate communication between a parent
window (Main Web) and a child iframe (Sub Web) through window.postMessage. The
communication flow works as follows:
Communication Flow
- Initialization: When the hook mounts, it listens for
INITmessage from the parent window containing game information (includingusername) - Start Game: After initialization, the hook sends
SHOW_LO5to request the first question - Question Received: Parent responds with
RETURN_LO5containing question data - Answer Submission: User selects an answer and calls
updateAnswer(), which sendsUPDATE_ANSWERwith the answer ID - Result Received: Parent responds with
RETURN_UPDATE_ANSWERcontainingisCorrect,isLastQuestion,correctAnswerId,incorrectAnswerId, and result data - Continue: User calls
handleContinue()which either requests the next question (SHOW_LO5) or marks the quiz as completed - Finish: When complete,
finish()sendsFINISHto signal completion (typically for iframe resizing)
Action Types
The bridge uses the following action constants (defined internally):
INIT: Main → Sub - Initialize game with user information (includesusername)SHOW_LO5: Sub → Main - Request a questionRETURN_LO5: Main → Sub - Send question dataUPDATE_ANSWER: Sub → Main - Submit selected answerRETURN_UPDATE_ANSWER: Main → Sub - Return answer resultFINISH: Sub → Main - Signal completion
Notes
- The hook automatically listens for
INITmessage from the parent window when mounted to receive game initialization data (including username) - The hook automatically requests the first question after initialization
- Answer history is tracked in the
answersarray, indexed by question number - The hook uses both
useStateanduseRefto ensure correct closure behavior across renders - All communication is asynchronous through postMessage events
- Make sure your parent window is set up to send
INITmessage with game data and handle the corresponding postMessage events
License
See package.json for license information.
