I am trying to write a quiz type module where you can select one of two answers.
const [active, setActive] = useState(true);
const toggle = () => {
setSelected(!selected)
}
<div id="quizAnswers">
<div className="answer {active ? 'active' : ''}">
{quiz.answers[0].text}
<button label="{active ? 'ACTIVE' : 'inactive'}" onClick={toggle}>
{active ? "ACTIVE" : "inactive"}
</button>
</div>
<div className="answer {active ? 'active' : ''}">
{quiz.answers[1].text}
<button label="{active ? 'ACTIVE' : 'inactive'}" onClick={toggle}>
{active ? 'ACTIVE' : 'inactive'}
</button>
</div>
This is about as far as I can figure out how to get. I am trying to get it so that when I click one button, the label and text switch to active, while switching the other to inactive, if it is currently active.
I've seen some stuff with react-DOM but I'm wondering if there is a more straight forward way to accomplish this? Thanks in advance.
CodePudding user response:
You can just have the state be the index of the active button, like Nicholas said in the comments:
// the default active button is the first button, button number 0
const [activeButton, setActiveButton] = useState(0);
const toggle = () => {
setSelected(!selected)
}
return(
<div id="quizAnswers">
<div className={`answer ${activeButton === 0 ? 'active' : ''}`}>
{quiz.answers[0].text}
<button label={`${activeButton === 0 ? 'ACTIVE' : 'inactive'}`} onClick={() => setActiveButton(0)}>
{active ? "ACTIVE" : "inactive"}
</button>
</div>
<div className={`answer ${activeButton === 1 ? 'active' : ''}`}>
{quiz.answers[1].text}
<button label={activeButton === 1 ? 'ACTIVE' : 'inactive'} onClick={() => setActiveButton(1)}>
{active ? 'ACTIVE' : 'inactive'}
</button>
</div>
)
And you can reduce duplication and make it generic to more than 2 buttons by using map
:
return(
<div id="quizAnswers">
{quiz.answers.map((answer, index) =>
<div className={`answer ${activeButton === index ? 'active' : ''}`}>
{answer.text}
<button label={activeButton === index ? 'ACTIVE' : 'inactive'} onClick={() => setActiveButton(index)}>
{active ? "ACTIVE" : "inactive"}
</button>
</div>
}
</div>
)
Also, note how I changed the syntax in className
and label
-- you were embedding {}
within double quotes (like " foo {bar} baz"
) which wouldn't work like you were expecting.
CodePudding user response:
Just as an additional example: you can hold a reference to the actual answer
object inside of your useState
variable. Also, it might be better to create a new component for actual Answer
and use it inside the .map
function. Or without map
function, but why to duplicate code?
const { useState, Fragment } = React;
const DUMMY_QUIZ = {
question: "How to do this?",
answers: [
{ id: 1, text: "Do that" },
{ id: 2, text: "Do this" }
]
};
function QuizAnswers() {
const [selectedAnswer, setSelectedAnswer] = useState();
const quiz = DUMMY_QUIZ;
return (
<div id="quizAnswers">
<p>Using map function</p>
{quiz.answers.map((a) => (
<QuizAnswer
item={a}
key={a.id}
onClick={() => setSelectedAnswer(a)}
isSelected={a === selectedAnswer}
/>
))}
<p>Without map function</p>
<Fragment>
<QuizAnswer
item={quiz.answers[0]}
onClick={() => setSelectedAnswer(quiz.answers[0])}
isSelected={quiz.answers[0] === selectedAnswer}
/>
<QuizAnswer
item={quiz.answers[1]}
onClick={() => setSelectedAnswer(quiz.answers[1])}
isSelected={quiz.answers[1] === selectedAnswer}
/>
</Fragment>
</div>
);
}
function QuizAnswer({ item, onClick, isSelected }) {
return (
<div className={`answer ${isSelected ? "active" : ""}`}>
{item.text}
<button label={`${isSelected ? "ACTIVE" : "inactive"}`} onClick={onClick}>
{isSelected ? "ACTIVE" : "inactive"}
</button>
</div>
);
}
ReactDOM.createRoot(document.getElementById("root")).render(<QuizAnswers />);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.1.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.1.0/umd/react-dom.development.js"></script>
<div id="root"></div>