Search code examples
javascriptcssreactjsindexingmap-function

How to select only one div at a time for quiz app answers options Reactjs


I'm creating a Quiz app in Reactjs,

The issue is When I try to select the one option it get selected but if I try to select the other one it also gets selected. I want if A is selected and if the user clicked on B the A will be unselected and B will get selected.

I'd tried to do it in the child component as well as the parent component. In Child, I'm able to select one div but if I click on the other option they are also getting selected. In Parent when I try to select any option it is selecting all of them.

Option.js (child component)

 import React, { useState } from 'react'


    function Option({ index, item, changeIndexValue }) {

    const [currentOptionSelected, setCurrentOptionSelected] = useState(false)

    const handleClick = (index) => {
        changeIndexValue(index)
        setCurrentOptionSelected(prevState => !prevState)

    }
    return (<>
        <div className='option'
            style={{ border: currentOptionSelected ? "2px solid red" : "1px solid black" }}
            onClick={() => handleClick(index)}
            key={index}
        ><p>{item}</p>
        </div>

    </>
        )
    }
    
    export default Option

QuizMain.js (Parent component)

    import React, { useState } from 'react'
import ProgressBar from '../ProgressBar/ProgressBar';
import './QuizMain.css'
import { useNavigate, useLocation } from 'react-router-dom';
import { useOutletContext } from "react-router-dom";
import ScoreCard from '../ScoreCard/ScoreCard';
import Option from '../Option/Option';



function QuizMain() {

    const [currentQuestion, setCurrentQuestion] = useState(0)
    const [changeCurrentQuestion, currentQuestionNumber, questionAre] = useOutletContext();
    const [finalReport, setFinalReport] = useState([]);
    const [finalReportVisible, setFinalReportVisible] = useState(false)
    const [correctAnswer, setCorrectAnswer] = useState("")
    const changeCorrectAnswer = (value) => {
        setCorrectAnswer(value)
    }
    const [indexValue, setIndexValue] = useState("")
    const changeIndexValue = (value) => {
        setIndexValue(value)
    }


    const location = useLocation();
    const { chooseQuizType } = location.state;
    console.log(chooseQuizType, 'location')


    const handleSubmit = () => {

        
        const answersCurrentQuestion = questionAre[currentQuestionNumber].options.map((item) => item)
        if (chooseQuizType == "withAnsers") {

            console.log(answersCurrentQuestion[indexValue], 'selected option')
            if (answersCurrentQuestion[indexValue] == questionAre[currentQuestionNumber].correctAnswer) {
                alert('Correct answer')
            } else {
                alert('Wrong answer')
                changeCorrectAnswer(questionAre[currentQuestionNumber].correctAnswer)
            }
        }
        setFinalReport(prevState => [...prevState, {
            question: questionAre[currentQuestionNumber].question,
            correctAnswer: questionAre[currentQuestionNumber].correctAnswer,
            yourAnswer: answersCurrentQuestion[indexValue]

        }])
        
    }

    return (<>
        {finalReportVisible ? <ScoreCard finalReport={finalReport} /> :
            <div className='quizMain'>
                
                <div className='quizMain__main'>
                    <div className='questions'>
                        <h3>{questionAre && questionAre[currentQuestionNumber]?.question}</h3>
                    </div>
                    <div className='options'>
                        {questionAre && questionAre[currentQuestionNumber]?.options.map((item, index) => {
                            return <Option
                                key={index}
                                item={item}
                                index={index}
                                changeCorrectAnswer={changeCorrectAnswer}
                                correctAnswer={correctAnswer}
                                indexValue={indexValue}
                                changeIndexValue={changeIndexValue}
                            />
                        })}
                        <p>{correctAnswer}</p>
                    </div>
                    <div className='submit__button'>
                        <button onClick={handleSubmit}>Submit</button>
                    </div>
                </div>
            </div >
        }
    </>
    )
    }
    
    export default QuizMain

Solution

  • In React, you should avoid putting too many logic in child components. Child components should be used to display information, let parent container handle logics.

    In QuizMain.js (Parent), define a state to controll which option is currently selected

    const [selected, setSelected] = useState(-1); // -1 means non is selected
    

    also define a function which will be passed to Option component (Child) to update selection

    const selectOption = (option) => {
        setSelected(option);
    }
    

    In Option.js (Child), include the following to props

    function Option({ index, item, onSelect, selected })
    

    for the onClick callback of Option.js, just put onSelect(index) to it

    onPress={ () => onSelect(index) }
    

    Back to QuizMain.js, each Option component should be like

    <Option
        ...
        key={ index }
        selected={ selected === index }
    />