[React]How to transform an object into an array inside a props[closed]

Hi everybody,

Sorry for bothering you again. Thanks to vipatron help, I managed to refractor my component but now I need to transform an object into an array to be able to continue working on it.

Here is my component :

class DisplayQuizz extends Component {
  constructor(props) {
    super(props);
    this.state = {
      reponses:  [],
      questions: [],
      questionElement: '',
      reponse: '',
      selectedResponse: 0,
      selectedReponseId: '',
      newStateTomerge: '',
      questionIndex: []

    };

  }

  handleClick(evt) {
    const questionToWhichThisAnswerChoiceBelongs = evt.target.id.split("-")[0];
    const newStateTomerge = {};
    newStateTomerge[questionToWhichThisAnswerChoiceBelongs] = evt.target.innerHTML;
    let responseId = parseInt(evt.target.id);
      this.setState({
        selectedResponseId: responseId,
        newStateTomerge: newStateTomerge
      });
  }
render() {
     const{test} = this.props;
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+"-"+reponse.id}
                   onClick={this.handleClick}
                 >
                   {reponse.reponse}
                 </MDBBtn>,
                 console.log(questionIndex),
                 test.questionElements[this.state.idOfBlank] = this.state.selectedReponseId
              ? test.reponses[this.state.selectedReponseId]
              : "vide",
            <span key={test.questionElements.id}>{test.questionElements.join("vide")}</span>

             )
           },
</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'>{test.questionElements.questionElement}</MDBCardText>
          <MDBCardText className='reponse' >{test.reponses.reponse}</MDBCardText>
      </div>
    );
  }

}

  export default DisplayQuizz;

Now, I have this error :
« TypeError: _this2.props.test.reponses[questionIndex].map is not a function »

After searching for several hours, I found it’s because questionIndex is an object.

So, my question is how can I transform the questionIndex inside « this.props.test.reponses[questionIndex].map » into an array ?

Thanks to have read me until now and thank you very much to the kind persons who maybe will help me. :slight_smile:

Edit : Finally my manager solved the problem. So this topic is closed now. Thanks everybody. :slight_smile:

you can use the for in loop to iterate over JS objects.

https://www.w3schools.com/jsref/jsref_forin.asp

Are you sure it’s not just a spelling mistake: it should be responses, not reponses, you’re currently trying to map over a nonexistent thing. And you’ve said questionIndex in the state is an array, so you seem to be trying to do responses[anArrayNotAnIndex]

Hi @Yumenotsuki (FYI, if You want people to know you are referencing them so they get a notification, you need to type the “@” symbol, and a list of forum members with the participants in that thread will pop up). Glad to see you’re making progress. You are correct that the error you’re getting is because _this2.props.test.reponses[questionIndex] doesn’t have a map function, i.e.: it isn’t an array. The real question I have is: is this ALL the code for your class component? Because right now, questionIndex is initialized to an empty array in the constructor.

In JS, all Arrays are subclasses of Objects, so instead of any string being a legal key, the keys in Arrays are all numbers. I believe what your code is trying to do is to coerce the array questionIndex ([]) into a string, looking up that key in responses, and failing to find any value. Thus, reponses[questionIndex].map() is the same as undefined.map().

I wrote a quick fiddle to show you what happens when you access empty members of an array or undefined properties on an object. Notice that when logging or printing the array, JS says an undefined “property/element” on an Array is an “empty slot”, not undefined, but when accessing that empty slot, the Array’s getter function returns undefined (which you can see with the console.log statements):
https://jsfiddle.net/vipatron/2pgeb3mx/2/

As I said above, we can’t see the code which you use to parse the JSON you are getting back from the API call from your first thread. That’s why we can’t help you better. Based on my understanding from that other thread, the JSON returns everything you need in order to display 1 question: all the questionElements combine to make 1 fill-in-the-blank question, and all the reponses are answer choices that are possible answers for that single question, and a reponseCorrecte which allows you to decide whether the user chose correctly. So, unless you are prefetching all the questions needed for the whole quiz, I can’t tell why you even need a questionIndex variable. Even if you are prefetching all the questions, wouldn’t questionIndex just be a Number, not an Array? If you can show us the code you are using to turn the JSON into state, that’d be very helpful.

It’s french. Original thread. But yeah, I gave the much more verbose version of your succinct answer because he’s fairly new, and a Japanese-primary speaker.

1 Like

Thanks all for your responses. :slight_smile:

So here is the parent component of DisplayQuiz :

Quiz :


import DisplayQuizz from  './components/displayQuizz';
import GenerateTest from  './components/generateTest';

const  afficheInit = {
  constat: null,
  instruction: null,
  questionElements: [],
  reponses: [],
  reponseCorrecte: null
}
const sessionId = document.getElementById("sessionId");
const appSession = sessionId.getAttribute('data');

class Quiz extends Component {
constructor(props) {
    super(props);
    this.state = {
      test:  afficheInit,
      appSession: appSession,
};
  }
componentDidMount() {
this.getTest(this.state);
  }
getTest(apiRequest) {
      fetch("https://someAPI.html", {
        method: 'POST',
        headers: {
          'Accept': 'application/json',
          'Content-Type': 'text/plain'
        },
        body: JSON.stringify(apiRequest),
      })
        .then(response => response.json())
        .then(data => {
          console.log(data)
          this.setState({
            test: data[0],
});
        });
  }
render() {
    if (appSession) {
return (
      <MDBCol>
      <MDBCard>
        <MDBCardImage className="img-fluid" src="https://mdbootstrap.com/img/Photos/Others/images/43.jpg" waves />
        <MDBCardBody>
          <DisplayQuizz test= {this.state.test} /> 
          <GenerateTest selectTest={() => this.getTest(this.state)}/> 
        </MDBCardBody>
      </MDBCard>
    </MDBCol>
);
  } else {
    return (
      <MDBCol>
      <MDBCard>
        <MDBCardImage className="img-fluid" src="https://mdbootstrap.com/img/Photos/Others/images/43.jpg" waves />
        <MDBCardBody>
          <MDBCardTitle>???</MDBCardTitle>
          <MDBCardText>
            Oh!
          </MDBCardText>
          <MDBBtn href="#">Hey !</MDBBtn>
        </MDBCardBody>
      </MDBCard>
    </MDBCol>

    );
  }
}
}

export default Quiz;

I hope it would help.

And yes I am a newbie with like 6 months of React. But I am a primarly French speaker and a she. What I am doing now for my internship is the front part of a japanese and french test for a website. :slight_smile:

1 Like

Sorry for the very short answer! Best to ignore me and listen to @vipatron for this :slight_smile:

2 Likes

Désolé. Deux choses que j’ai remarqué:

  1. Dans le constructeur, quand vous declarez this.state, vous n’avez pas initialisé les propriétés dedans avec les valeurs contenues dans le JSON. Il vous faut toujours écrire ce code. Vous pouvez accéder aux données comme ça:
this.state = {
  reponses = this.props.test.reponses // Remarquez: chaque élément de ce tableau sera un objet.
};
  1. Dans handleClick(), vous assignez à this.state un propriété qui vous appellez this.state.newStateToMerge, et je suis sur que vous n’avez pas voulu le faire. Le paramètre qu’on passe à this.setState() doit etre un object. Chaque propriété qui n’était pas un “ownProperty” de this.state sera ajouté. Chaque propriété qui était là avant l’appel à this.setState() sera remplacé.
    Edit: Si vous avez lu ce que j’ai écrit par passant plusieurs objets à this.setState, j’avais tort. On peut seulement passer un object, alors il faut assigner les propriété de newStateTomerge à un nouvelle objet, ou assigner selectedResponseId à newStateTomerge avant le passant à this.setState.

J’espère que mes explications en français ont du sens. Le vocabulaire des concepts d’ordinateurs de Microsoft m’a aidé tellement.

1 Like

No problem. Thank you anyway. :slight_smile:

Merci beaucoup d’avoir pris le temps de traduire la réponse en français. :smile: (Thank you very much to have taken the time to translate your answer in French.) You have a very good level in French to have been able to do it.

I will follow your advice and work on it.

Edit : So today, I tried to do add the props from my JSON to the state of my DisplayQuizz component. I hope I did it right.

class DisplayQuizz extends Component {
  constructor(props) {
    super(props);
    this.state = {
      reponses:  this.props.test.reponses,
      questionElements: this.props.test.questionElements,
      questionElement: this.props.test.questionElements.questionElement,
      questionElementId: this.props.test.questionElements.id,
      reponse: this.props.test.reponses.reponse,
      reponseId: this.props.test.reponses.id,
      selectedReponse: 0,
      selectedReponseId: '',
      questionIndex: [],
    };
  }

As I don’t know what to do with the newStateToMerge in handleClick as it need to become an object, I just put it in comment for now and will continue to think what to do with it.

But, at the moment, I think my main problem is this :

{this.props.test.reponses[questionIndex].map(
               reponse =>
                 <MDBBtn
                   className='response'
                   key={reponse.id}
                   id={questionElement.id+"-"+reponse.id}
                   onClick={this.handleClick}
                 >
                   {reponse.reponse}
                 </MDBBtn>,
                 console.log(questionIndex)

It still throwing the « TypeError: _this2.props.test.reponses[questionIndex].map is not a function » and I know that it’s because of the questionIndex (which is equal to zero when I console.log it) but I can’t seem to find how to resolve this. However, when I delete the questionIndex in props.test.reponses, even if my responses don’t display, the console.log of questionIndex display numbers which are the same numbers as the questionElement.ids. Is this normal ? What do I need to do first to be able to resolve this error ? (I think I need to resolve this error first before trying to correct the possible wrong other things in my code. But maybe I am wrong)

Sorry I missed your edit. (I guess edits don’t send notifications). I don’t know whether you’re still working on this. My first guess is the the error means that this.props.test.reponses[questionIndex] isn’t an Array.

Thank you for your message. In fact I am not working on this anymore because my manager found the problem.