Search code examples
javascriptreactjsstatereact-statedisable

Disable individual buttons in React based on Id?


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>
 
    )
}```

Solution

  • 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>
      </>
    }