skip to Main Content

I have 4 buttons created through a map. They are the four possible answers of a quizz application.

Their state is upadated through one function handleClick. On click, the function disables the buttons, checks if the answer is true, then generates a new question, with new answers inside the buttons, then reactivates the buttons. The idea is that the user cannot select multiple answers.

The problem is that when i change the disabled state of my buttons, they rerender, generating instantly a new question.

The buttons & the onclick :

const createReponse = () =>
    tableauDeReponse
      .sort((a, b) => a - b)
      .map((ele) => {
        return (
          <button
          disabled={disableButton}
            className={`Reponse-container`}
            onClick={handleClick}
            type="button"
            id={ele.toString()}
            key={ele}
          >
            {ele}
          </button>
        );
      });

const handleClick = (e) => {  
    console.log("click");
 
    //  setDisableButton(true) this creates the unwanted rerender
    if (Number(e.target.id) === bonneReponse) { //this changes the class depending if the answer is ture or not
      
      e.target.className = "Reponse-container goodAnswer";
    } else {
      e.target.className = "Reponse-container wrongAnswer";
    }
         
    delay(1500).then(() => {
       getMovie() //this creates the question and answers
       e.target.className = "Reponse-container"
     
    //  setDisableButton(true) this creates the unwanted rerender
  }  )
}

I have tried basically every tutorial i could find about conditonal rendering and preventing rendering, but did poorly so far.

Thanks in advance 🙂

4

Answers


  1. Chosen as BEST ANSWER

    So I got to review my code with a pro and he actually changed a bunch of stuff in diffrent components in order to make the whole thing work, I had const in my components instead of state was mostly the problem apparently.


  2. I think you can achieve this by creating a new state selectedAnsId using below code.

    const createReponse = () =>
       tableauDeReponse
        .sort((a, b) => a - b)
        .map((ele) => {
        return (
          <button
            disabled={disableButton}
            className={`Reponse-container ${ selectedAnsId === ele.toString() ? ele===correctAnswer ? "goodAnswer" :  "wrongAnswer" : ""}`}
            onClick={handleClick}
            type="button"
            id={ele.toString()}
            key={ele}
          >
            {ele}
          </button>
        );
     });
    
    const handleClick = (e) => {  
      setDisableButton(true)
      setSelectedAnsId(e.target.id)
      // these two setState should be batched now
     
      delay(1500).then(() => {
       getMovie() //this creates the question and answers
       e.target.className = "Reponse-container";
      })
    }
    
    Login or Signup to reply.
  3. It seems that your ‘generate a new question’ side effect is somehow executing after users interact with your button. I can’t seem to replicate this from your current code.

    However, you should be setting your button state (both disabled and classname) after your new question fetch or getMovie function has completed.

    For example:

      const generateNewQuestion = (e) => {
        setTimeout(() => {
          setTotalQuestions(totalQuestions + 1);
          setDisableButton(false); // Enable your buttons to cause your rerender after all data fetching is complete
          e.target.className = "Reponse-container"; // Reset styles very last
        }, 1500);
      };
    

    See a working example here https://codesandbox.io/s/react-prevent-rendering-of-mapped-buttons-chd130?file=/src/App.js

    Login or Signup to reply.
  4. The issue with the buttons rerendering and instantly generating a new question is caused by the state change of disableButton inside the handleClick function. When you update the state, it triggers a rerender of the component, including the buttons.

    To avoid this unwanted rerender, you can move the state of disableButton to be managed by a parent component that holds the createReponse function. Then, you can pass down the state and a function to update it as props to the buttons. This way, the state update will not trigger a rerender of the buttons.

    Here’s an example of how you can do this:

    // Parent component
    const Quiz = () => {
      const [disableButton, setDisableButton] = useState(false);
    
      const handleButtonClick = (e) => {
        // Disable all buttons
        setDisableButton(true);
    
        // Check if answer is correct
        if (Number(e.target.id) === bonneReponse) {
          e.target.className = "Reponse-container goodAnswer";
        } else {
          e.target.className = "Reponse-container wrongAnswer";
        }
    
        // Wait 1.5 seconds before generating new question
        delay(1500).then(() => {
          // Generate new question and answers
          getMovie();
    
          // Reset all button classes and enable them
          const buttons = document.querySelectorAll(".Reponse-container");
          buttons.forEach((button) => {
            button.className = "Reponse-container";
          });
          setDisableButton(false);
        });
      };
    
      const createReponse = () =>
        tableauDeReponse
          .sort((a, b) => a - b)
          .map((ele) => {
            return (
              <button
                disabled={disableButton}
                className={`Reponse-container`}
                onClick={handleButtonClick}
                type="button"
                id={ele.toString()}
                key={ele}
              >
                {ele}
              </button>
            );
          });
    
      return (
        <div>
          {createReponse()}
        </div>
      );
    };
    

    In this example, the state of disableButton is managed by the Quiz component. When a button is clicked, the handleButtonClick function is called, which first disables all buttons by updating the state. Then, it checks if the answer is correct and updates the class of the clicked button accordingly. After 1.5 seconds, it generates a new question, resets all button classes, and enables them by updating the state.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search