I am trying to build a multiple choice quiz app using React. I am using state to increment the user's score when the correct answer button is clicked however I want the user to be able to 'click' the correct button once as a selection. I have found if the user clicks the correct answer button multiple times each click increases their score.
I have tried to apply a disable key to each of the buttons and use state to change the state to true once the button is clicked however unfortunately this affects every button.
Is there a way I can incorporate the ID in the question Array?
I want each individual answer button to be disabled once clicked.
My code so far:
const questions = [
{
questionText: 'What is the Capital of France?',
answerOptions: [
{ id: 0, answerText: 'London', isCorrect: false },
{ id: 1, answerText: 'Paris', isCorrect: true },
{ id: 2, answerText: 'Nice', isCorrect: false },
{ id: 3, answerText: 'Lyon', isCorrect: false },
],
},
{
questionText: 'What is the Capital of the UK?',
answerOptions: [
{ id: 0, answerText: 'London', isCorrect: true },
{ id: 1, answerText: 'New York', isCorrect: false },
{ id: 2, answerText: 'Sydney', isCorrect: false },
{ id: 3, answerText: 'Bath', isCorrect: false },
],
},
{
questionText: 'Who turns out to be the true victor in the Battle of Armageddon in Mortal Kombat?',
answerOptions: [
{ id: 0, answerText: 'Liu Kang', isCorrect: false },
{ id: 1, answerText: 'Shang Tsung', isCorrect: false },
{ id: 2, answerText: 'Raiden', isCorrect: false },
{ id: 3, answerText: 'Shao Kahn', isCorrect: true },
],
},
{
questionText: 'Under what pseudonym did Stephen King publish five novels between 1977 and 1984?',
answerOptions: [
{ id: 0, answerText: 'Richard Bachman', isCorrect: true },
{ id: 1, answerText: 'J. D. Robb', isCorrect: false },
{ id: 2, answerText: 'Mark Twain', isCorrect: false },
{ id: 3, answerText: 'Lewis Carroll', isCorrect: false },
],
},
];
const [startQuiz, setStartQuiz] = useState(true);
function startClick() {
setStartQuiz(current => !current);
}
const [score, setScore] = useState(0);
const optionClicked = (isCorrect) => {
if (isCorrect) {
setScore(score + 1);
}
}
const [disabled, setDisabled] = useState(false);
const [showResults, setShowResults] = useState(true);
function resultsClick() {
setShowResults(current => !current);
}
function restartQuiz() {
setStartQuiz(current => !current);
setShowResults(current => true);
setScore(0);
setDisabled(false)
}
return(
<div>
{startQuiz ?
<div className="start-screen">
<div className="start-text">
<h1>Quizzical</h1>
<p>Test your knowledge!</p>
<button onClick={startClick}>Start Quiz</button>
</div>
</div>
:
<div className="quiz">
<div className="firstQuestion">
<h2>{questions[0].questionText}</h2>
</div>
<div className="firstAnswers">
{questions[0].answerOptions.map((answerOption) => {
return (
<button key={answerOption.id}
disabled={disabled}
onClick={() => {
optionClicked(answerOption.isCorrect);
setDisabled(true);
}}>
{answerOption.answerText}
</button>
);
}
)}
</div>
<div className="secondQuestion">
<h2>{questions[1].questionText}</h2>
</div>
<div className="secondAnswers">
{questions[1].answerOptions.map((answerOption) => {
return (
<button key={answerOption.id}
disabled={disabled}
onClick={() => {
optionClicked(answerOption.isCorrect)
setDisabled(true);
}}>
{answerOption.answerText}
</button>
);
}
)}
</div>
<div className="thirdQuestion">
<h2>{questions[2].questionText}</h2>
</div>
<div className="thirdAnswers">
{questions[2].answerOptions.map((answerOption) => {
return (
<button key={answerOption.id}
disabled={disabled}
onClick={() => {
optionClicked(answerOption.isCorrect)
setDisabled(true)
}}>
{answerOption.answerText}
</button>
);
}
)}
</div>
<div className="fourthQuestion">
<h2>{questions[3].questionText}</h2>
</div>
<div className="fourthAnswers">
{questions[3].answerOptions.map((answerOption) => {
return (
<button key={answerOption.id}
disabled={disabled}
onClick={() => {
optionClicked(answerOption.isCorrect)
setDisabled(true)
}}>
{answerOption.answerText}
</button>
);
}
)}
</div>
<br></br>
<div className="resultsSection">
<button onClick={()=> resultsClick()}>
Check Answers!
</button>
<h3 className="resultsText">{showResults ? <p>Score:</p> : <p>Score: {score}/4</p>}</h3>
<button onClick={restartQuiz}>Restart Quiz</button>
</div>
</div>
}
</div>
)
}```
First and foremost, it would be great if you could create a reusable component for each set of question+answers to avoid writing too much repeated code e.g.
function Question({questionText, answerOptions}) {
return <>
<div className="question">
<h2>{questionText}</h2>
</div>
<div className="answers">
{answerOptions.map((answerOption) => {
return (
<button key={answerOption.id}
disabled={disabled}
onClick={() => {
optionClicked(answerOption.isCorrect)
setDisabled(true)
}}>
{answerOption.answerText}
</button>
)
}
)}
</div>
</>
}
Next, you could use the useState hook to store an array of numbers indicating which answer option has been clicked:
const [disabledOptions, setDisabledOptions] = useState([])
Edit the optionClicked function such that it receives both the option id and the isCorrect boolean, and add the option id to the disabledOptions array
const optionClicked = (optionId, isCorrect) => {
const newDisabledArray = [...disabledOptions, optionId];
setDisabledOptions(newDisabledArray);
if (isCorrect){
setScore(score + 1);
}
}
Final code will look something like this:
function Question({questionText, answerOptions}) {
const [disabledOptions, setDisabledOptions] = useState([])
const optionClicked = (optionId, isCorrect) => {
const newDisabledArray = [...disabledOptions, optionId];
setDisabledOptions(newDisabledArray);
if (isCorrect){
setScore(score + 1);
}
}
return <>
<div className="question">
<h2>{questionText}</h2>
</div>
<div className="answers">
{answerOptions.map((answerOption) => {
const {id, isCorrect, answerText} = answerOption;
return (
<button key={id}
disabled={disabledOptions.includes(id)}
onClick={() => {
optionClicked(id, isCorrect)
}}>
{answerText}
</button>
)
}
)}
</div>
</>
}