Home > Net >  How to only select one button at a time with React? Without radio buttons
How to only select one button at a time with React? Without radio buttons

Time:09-20

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>

  • Related