[react, hooks] How to select an answer among several anwsers and replace the empty question part with the choosen answer ?[closed]

Hi everybody

For my internship, I have to do a function who selects an answer among several anwsers and replace the empty question part with the selected anwser.

For example :

How old ____you ?

a) are b) is c) have d)nothing

If the user clicks on a) answer, its replaces « ____ » in the question.

So final result would be like this :

How old are you ?

a) are b) is c) have d)nothing

But after thinking about it for several days, unfortunatly, I am still stuck so I am here to ask some kind people a bit of help.

So, here is the component I want to work with :

DisplayQuizz.jsx

const DisplayQuizz = ({test}) => {
  const questions = test.questionElements.map(questionElement => <span key={questionElement.id}>{questionElement.questionElement} </span> )

  const displayReponses = test.reponses.map(reponse => <MDBBtn className='response' key={reponse.id}>{reponse.reponse} </MDBBtn>)
return (
      <div className="DisplayTest">
          <MDBCardTitle className= 'super'>{test.appInstruction}</MDBCardTitle>
          <MDBCardTitle className= 'superclasse'>
            {test.constat}
          </MDBCardTitle>
          <MDBCardTitle className= 'super'>{test.instruction}</MDBCardTitle>
          <MDBCardText className='question'>{questions}</MDBCardText>
          <MDBCardText className='reponse' onClick={() => getAnswer()} >{displayReponses}</MDBCardText>
      </div>
    );
}

  export default DisplayQuizz;

Firstly, I want the question and answers to interact with eachother so I was thinking I need a state in my component. So, I think I can choose between two answers :

A) I create a class component instead of my functional component. However, what I need to use to make my function will be in the render and I need it before the render.

B) I add Hooks to my component. But as Hooks is a new thing, there are not that much documentations to help me yet. Moreover, as I have been coding for less than a year with and without React, I am not a master of React yet and Hooks uses a different king of writing, so I am a bit confused. But I think, for this case, it is better to use Hooks. What do you think ?

Secondly, I think to be able to do my function, I need first to be able to select the choosen answer and then to replace the empty question space with it. I am right ?

So, I tried several things with not sucess. In addition, I think the biggest problem is how to trigger or get just one answer and not all from the map function.

Here is what I have done until now :

import React, { useState, useEffect  } from 'react';
const DisplayQuizz = ({test}) => {
  const questions = test.questionElements.map(questionElement => <span key={questionElement.id}>{questionElement.questionElement} </span> )

  const displayReponses = test.reponses.map(reponse => <MDBBtn className='response' key={reponse.id}>{reponse.reponse} </MDBBtn>)
const [isSelected, setSelected] = useState(true);
function useAnswerSelected() {
  setSelected(prevState => ({
        isSelected: !prevState.isSelected,
    }));

      getAnswer()
      console.log("Bien joué", isSelected);
}
function getAnswer() {
if(isSelected === true) {

    const selectedAnswer = test.reponses.map((reponse, id) => {
        return (
          <li key={id} onClick={() => useAnswerSelected(reponse.reponse)}>
          </li>,
          console.log(reponse.reponse, id)
        );
    });
  }
}
return (
      <div className="DisplayTest">
          <MDBCardTitle className= 'super'>{test.appInstruction}</MDBCardTitle>
          <MDBCardTitle className= 'superclasse'>
            {test.constat}
          </MDBCardTitle>
          <MDBCardTitle className= 'super'>{test.instruction}</MDBCardTitle>
          <MDBCardText className='question'>{questions}</MDBCardText>
          <MDBCardText className='reponse' onClick={() => getAnswer()} >{displayReponses}</MDBCardText>
      </div>
    );
}

  export default DisplayQuizz;

Not very good in my opinion. But I admit that I am thinking so much about it that I am very confused and lost now. If someone could tell me by which thing I need to begin with, I would be very grateful.

Lastly, when I was searching for some solution on internet to help me, I did not see any React quiz app who would be replacing an empty space question by a choosen answer. So I came to think maybe is it not possible ? What do you think ?

I thank all of you to have read my post until here and I hope some people could help me a bit with my problem.

I would argue that if you have only been coding for one year total, and can’t solve this problem with classes (stateful components), then you absolutely should not try to solve it with Hooks. I believe that mastery comes from the ground up. There is very little in React that isn’t explained by understanding ES2015. You are correct in thinking that new things aren’t well-documented (places like StackOverflow have tons of parallel examples for class components, very few for Hooks (yet)).

After reading your code a little further and trying to write it in a simple class component, I’ve run up against a small roadblock: I don’t know how the data you’re passing to DisplayQuizz is organized. Does a response’s reponse.id correlate with the relevant question’s questionElement.id? If so, this whole exercise is completely doable.

Edit: Also, looking at what you return/render, it appears that you are rendering ALL the questions first, THEN ALL the responses. How many responses are there? 4q, where q is the number of questions, or are there only 4 responses total, and they are the same for every question?

<div className="DisplayTest">
	<MDBCardTitle className= 'super'>{test.appInstruction}</MDBCardTitle>
	<MDBCardTitle className= 'superclasse'>
		{test.constat}
	</MDBCardTitle>
	<MDBCardTitle className= 'super'>{test.instruction}</MDBCardTitle>
	<MDBCardText className='question'>{questions}</MDBCardText>
	<MDBCardText className='reponse' onClick={() => getAnswer()} >{displayReponses}</MDBCardText>
</div>

So, without knowing more, here’s a basic structure for you to follow and tweak:

import React, { useState, useEffect  } from 'react';
const DisplayQuizz extends React.Component {
  constructor(props){
    super(props);
    this.state = {
    }
  }


    function handleClick(evt) {
      const questionToWhichThisAnswerChoiceBelongs = evt.target.id.split("-")[0]];
      const newStateTomerge = {};
      newStateTomerge[questionToWhichThisAnswerChoiceBelongs] = evt.target.innerHTML;
      setState(newStateTomerge);
    }

  
  render (){
    // const questions = this.props.test.questionElements.map(
      // questionElement => <span key={questionElement.id}>{questionElement.questionElement.replace("____ ", this.state.)} </span>
    // )

    // const displayReponses = this.props.test.reponses.map(
      // reponse => <MDBBtn className='response' key={reponse.id}>{reponse.reponse} </MDBBtn>
    // )
    const questionsAndResponses = this.props.test.questionElements.map(
      (questionElement, questionIndex) =>
        <MDBCardText className='question'>
          <span key={questionElement.id}>
            {questionElement.questionElement.replace("____", this.state[questionElement.id])}
          </span>
          { this.props.test.reponses[questionIndex].map(
              reponse =>
                <MDBBtn
                  className='response'
                  key={reponse.id}
                  id={questionElement.id+"-"+response.id}//or some other attribute that can identify the HTML element you will eventually render (which will respond to a click event).
                  onClick={this.handleClick}
                >
                  {reponse.reponse}
                </MDBBtn>
            )
          }
        </MDBCardText>;
    )
    
    return (
        <div className="DisplayTest">
            <MDBCardTitle className= 'super'>{test.appInstruction}</MDBCardTitle>
            <MDBCardTitle className= 'superclasse'>
              {test.constat}
            </MDBCardTitle>
            <MDBCardTitle className= 'super'>{test.instruction}</MDBCardTitle>
            <MDBCardText className='question'>{questions}</MDBCardText>
            <MDBCardText className='reponse' onClick={useAnswerSelected} >{displayReponses}</MDBCardText>
        </div>
      );
  }
}

  export default DisplayQuizz;

Thank you very much for your response.

In fact, I think I agree with you and I think it would be more convienent for me to use a stateful component, if it is possible.

In fact, I fetch my data in another component. Here is the JSON I get, when I am fetching data :

[ { "appMode": "evaluation", "appEtape": "init", "appInstruction": "Bienvenue. Vous allez devoir r\u00e9pondre \u00e0 des questions qui permettront de d\u00e9terminer votre niveau.", "constat": "Voici la question", "instruction": "C'est la question test", "questionElements": [ { "id": 1, "questionElement": "\u3053\u308c\u306f" }, { "id": 2, "questionElement": "vide" }, { "id": 3, "questionElement": "\u3067\u3059\u304b\u3002" } ], "reponses": [ { "id": 1, "reponse": "\u3071\u3093" }, { "id": 2, "reponse": "\u306b" }, { "id": 3, "reponse": "\u306a\u3093" }, { "id": 4, "reponse": "\u306a\u3093" } ], "reponseCorrecte": "\u306a\u3093" } ]

I hope it would help.

To answer your question, yes for now I render all the API data (there is only one question for now). The number of answers will vary for each question (for exemple one question will have three answers to choose from and another question will have four).

For the question, I will fetch them one by one. When the user choose a response, he will have to click on a submit button who will fetch the next question.

I hope my answer will help and thank you to have take some of your time to answer my problem (and sorry for my weird English)

EDIT : Sorry to have take so much time to answer. English is not my mother tongue, sorry and thank you for your answer

I thought I’d be able to understand more, but I only speak French and English, not Japanese. Since both questionElements and reponses in the example JSON are arrays, but of different sizes, (3 question elements and 4 responses, and most of the “questionElements” are made of Hiragana unicode characters, I have to ask: is each “questionElement” a WHOLE QUESTION? Or is a fragment of a question—to use your earlier example, “How old ____you?”, would that be:

questionElements:[
  {id: 1, questionElement: "How old"},
  {id: 2, questionElement: "vide"}, // "vide" means "empty" in french, so is this the blank???
  {id: 3, questionElement: "you?"}
]

Ah sorry, I didn’t think about changing the japanese words and french words.

In each questionElements is a fragment of a question.
And yes “vide” is the blank space.

So this is very easy:
A minimal “state” is what changes based on user input.
It looks like your state can be described minimally by holding on to 2 pieces of information:

  1. which questionElement equals "vide" (the id number of that questionelement, unless it is ALWAYS 2, in which case it isn’t state because it doesn’t vary over time.
  2. which answer choice has been selected by the user.

Everything else is declarative. In your render method, before you return, have some code like this: (assume that “no answer selected” means that this.state.selectedReponse = 0, aka the initial state)

questionElements[this.state.idOfBlank] = this.state.selectedReponseId
  ? reponses[this.state.selectedReponseId]
  : "______";
question = <span key={questionElement.id}>questionElements.join("")</span>

Your click handler should have something like:
(assuming the clickable element’s id contains a number corresponding to the reponse.id in the JSON, like “reponse-2”)

let responseId = parseInt(evt.target.id);//see assumption above.
setState({selectedResponseId: responseId})

Hopefully you see why React is declarative. You declare explicitly what you want to be rendered to the screen as a function of state. You update that state based on user interaction (which answer they clicked) and props (the JSON returned from the API call).


Now, assuming this whole test is to be a single-page application, the whole JSON returned from the API belongs in state (as variables, not JSON), because which question is displayed will *vary over time based on user interaction* (the definition of *state*), but the core code for your initial problem will be the same.

Thank you very much for your answer. I will study it because I need some time to understand it all fine.

Yes, you guess right : which questionElement is equal to “vide” will depend on the question and no answer will be equal to 0.

I think it will be a single page application. But it will be like a plugin to a ModX, Apache website.

1 Like