0

I am making a quiz as a part of larger quiz website. The basic idea is that when a user clicks on an answer with text generated by the questions object, the associated 'type' string in the object is added to the state 'score' array. At the end, I use a for loop to determine which answer string was most frequently chosen, giving the result.

My problem is that the score array won't fill correctly with those strings, and i'm not entirely sure what is going on. I know you shouldn't try to alter the state directly, and should either do your conversion outside of the setState, or use iteration.

When I try to use iteration though - A.K.A something like this:

    export default class Quiz3 extends React.Component {
      constructor(props) {
        super(props)
        this.state = {
          clickBegin: false,
          scoredQuiz: false,
          currentQuestion: 0,
          score: [],
          mostFrequent: '',
        }
          this.functionA = this.functionA.bind(this);
      }
            functionA () {
    var stateScore = this.state.score;
    if(this.state.currentQuestion < 7) {
      this.setState ({
        score: [...stateScore, questions[this.state.currentQuestion].choiceA.type], 
        currentQuestion: this.state.currentQuestion + 1
      })
    }
    else if(this.state.currentQuestion >= 7) {
      this.scoreQuiz(); 
    }
    return;
  }

I get a TypeError that says that this.state.score can not be iterated.

Okay. So I try manipulating it outside out the setState with .concat (also tried push, but that manipulates the state directly, which causes issues).

export default class Quiz3 extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      clickBegin: false,
      scoredQuiz: false,
      currentQuestion: 0,
      score: [],
      mostFrequent: '',
    }
    this.functionA = this.functionA.bind(this);
  }

  functionA () {
    var stateScore = this.state.score;
    if(this.state.currentQuestion < 7) {
      var newScore = stateScore.concat(questions[this.state.currentQuestion].choiceA.type)
      this.setState ({
        score: newScore, 
        currentQuestion: this.state.currentQuestion + 1
      })
    }
    else if(this.state.currentQuestion >= 7) {
      this.scoreQuiz(); 
    }
    return;
  }

Then that says that the TypeError is that stateScore.concat() is not a function. I didn't know it had to be, or how to correct that error.

Long story short, I'm trying to populate the score state array with values from an outside object, and I can't. There are also more functions that I made, but I wanted to leave them out of these main examples for the sake of simplicity. Here's the full code in case I missed something. (I know that function B-D are using push. I was going to copy the code over later from functionA)

quiz3.js (The React app itself):

import React from 'react'; 
import TopNavbar from '../navbar';
import {startLabels} from './javascript/javascript-quiz-3'; 
import {questions} from './javascript/javascript-quiz-3';
import {endLabels} from './javascript/javascript-quiz-3'; 
import {Container} from 'react-bootstrap'; 
import {Row} from 'react-bootstrap';
import {Col} from 'react-bootstrap';

export default class Quiz3 extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      clickBegin: false,
      scoredQuiz: false,
      currentQuestion: 0,
      score: [],
      mostFrequent: '',
    }
    this.reset = this.reset.bind(this); 
    this.changeClicked = this.changeClicked.bind(this); 
    this.scoreQuiz = this.scoreQuiz.bind(this); 
    this.functionA = this.functionA.bind(this);
    this.functionB = this.functionB.bind(this);
    this.functionC = this.functionC.bind(this);
    this.functionD = this.functionD.bind(this); 
  }

  // State Functions

  reset() {
    this.setState({
      currentQuestion: 0,
      score: 0,
    })
  }

  changeClicked() {
    if (this.state.clickBegin === false) {
      this.setState({
        clickBegin: true
      })
    }
    else {
      this.setState({
        clickBegin: false
      })
    console.log(this.state.clickBegin)
    }
  }

  scoreQuiz() {
    if(this.state.currentQuestion === 7) {
      if(this.state.scoredQuiz === false) {
        this.setState({
          scoredQuiz: true
        })
      }
      for(var i = 0; i > this.state.score.length; i++) {
        var counts = {}; 
        var compare = 0; 
        var result = this.state.score[i];
        var mostFrequentValue = undefined;

        if (counts[result] === undefined) {
          counts[result] = 1
        }
        else {
          counts[result] = counts[result] + 1
        }

        if (counts[result] > compare) {
          compare = counts[result]; 
          mostFrequentValue = this.state.score[i]; 
        }
      }
      this.setState({
        mostFrequent: this.state.score[mostFrequentValue]
      })
    }
    else {
      this.setState({
        scoredQuiz: false
      })
    }
  }

  functionA () {
    var stateScore = this.state.score;
    if(this.state.currentQuestion < 7) {
      var newScore = stateScore.concat(questions[this.state.currentQuestion].choiceA.type)
      this.setState ({
        score: newScore, 
        currentQuestion: this.state.currentQuestion + 1
      })
    }
    else if(this.state.currentQuestion >= 7) {
      this.scoreQuiz(); 
    }
    return;
  }

  functionB () {
    if(this.state.currentQuestion < 7) {
      this.setState ({
        score: this.state.score.push(questions[this.state.currentQuestion].choiceB.type),
        currentQuestion: this.state.currentQuestion + 1
      })
    }
    else if(this.state.currentQuestion >= 7) {
      this.scoreQuiz();
    }
    return;
  }

  functionC () {
    if(this.state.currentQuestion < 7) {
      this.setState ({
        score: this.state.score.push(questions[this.state.currentQuestion].choiceC.type),
        currentQuestion: this.state.currentQuestion + 1
      })
    }
    else if(this.state.currentQuestion >= 7) {
      this.scoreQuiz();
    }
    return;
  }

  functionD () {
    if(this.state.currentQuestion < 7) {
      this.setState ({
        score: this.state.score.push(questions[this.state.currentQuestion].choiceD.type),
        currentQuestion: this.state.currentQuestion + 1
      })
    }
    else if(this.state.currentQuestion >= 7) {
      this.scoreQuiz();
    }
    return;
  }

// Different pages rendered depending on state values

  render() {
    if (this.state.clickBegin === false && this.state.scoredQuiz === false) {
      return (
        <div>
          <TopNavbar />
          <div id = "main">
            <StartScreen 
            clickBegin = {this.state.clickBegin}
            scoredQuiz = {this.state.scoredQuiz}
            currentQuestion = {this.state.currentQuestion}
            score = {this.state.score}
            changeClicked = {this.changeClicked} 
            reset = {this.reset} />
          </div>
        </div>
      )
    }
    if (this.state.clickBegin === true && this.state.scoredQuiz === false) {
      return (
        <div id = "main">
          <TopNavbar />
          <QuestionsScreen 
            clickBegin = {this.state.clickBegin}
            scoredQuiz = {this.state.scoredQuiz}
            currentQuestion = {this.state.currentQuestion} 
            score = {this.state.score}
            functionA = {this.functionA}
            functionB = {this.functionB}
            functionC = {this.functionC}
            functionD = {this.functionD} 
          />
        </div>
      )
    }

    if (this.state.clickBegin === true && this.state.scoredQuiz === true) {
      return (
        <div>
          <TopNavbar />
          <div id = "main">
            <EndScreen 
              currentQuestion = {this.state.currentQuestion}
              score = {this.state.score}
              mostFrequent = {this.state.mostFrequent}
              scoreQuiz = {this.scoreQuiz} 
              reset = {this.reset}
            />
          </div>
        </div>
      )
    }
    else {
      return null;
    }
  }
}

class StartScreen extends React.Component {
  constructor(props) {
    super(props)
    this.click = this.click.bind(this);
  }
  click() {
    this.props.changeClicked();
    this.props.reset(); 
  }
  render() {
    return (
      <Container fluid id = 'start-screen'>
        <Row id = "quiz-title"> 
          <h1> {startLabels[0].title} </h1> 
        </Row>
        <Row>
          <img src = {startLabels[0].imgSrc} /> 
        </Row>
        <Row id = "quiz-description"> 
          <p> {startLabels[0].descrip} </p> 
        </Row>
        <Row id = "begin-quiz" onClick = {this.click}> 
          <p> {startLabels[0].startQuiz} </p> 
        </Row> 
      </Container>
    )
  }
}

class QuestionsScreen extends React.Component {
  render() {
    return (
      <Container fluid id = "questions-screen"> 
        <Row id = "question-title"> 
          <p> {questions[this.props.currentQuestion].question} </p> 
        </Row>
        <Row id = "choice-container-1"> 
          <Col id = "choiceA" onClick = {this.props.functionA}> <p> {questions[this.props.currentQuestion].choiceA.choice} </p> </Col>
          <Col id = "choiceB" onClick = {this.props.functionB}> <p> {questions[this.props.currentQuestion].choiceB.choice} </p> </Col>
        </Row>
        <Row id = "choice-container-2">
          <Col id = "choiceC" onClick = {this.props.functionC}> <p> {questions[this.props.currentQuestion].choiceC.choice} </p> </Col>
          <Col id = "choiceD" onClick = {this.props.functionD}> <p> {questions[this.props.currentQuestion].choiceD.choice} </p> </Col>
        </Row>
      </Container>
    )
  }
}

class EndScreen extends React.Component {
  render() {
    if (this.props.currentQuestion === 7 && this.props.mostFrequent === "Warrior") {
      return (
        <Container fluid id = "end-screen"> 
          <Row id = "result"> <h1> {endLabels[0].result} </h1> </Row>
          <Row id = "result-picture"> <img src = {endLabels[0].imgSrc} /> </Row>
          <Row id = "result-description"> <p> {endLabels[0].descrip} </p> </Row>
          <Row id = "take-again" onclick = {this.props.reset}> <p> {endLabels[0].takeAgain} </p> </Row>
        </Container>
      )
    }
    if (this.props.currentQuestion === 7 && this.props.mostFrequent === "Rogue") {
      return (
        <Container fluid id = "end-screen"> 
          <Row id = "result"> <h1> {endLabels[1].result} </h1> </Row>
          <Row id = "result-picture"> <img src = {endLabels[1].imgSrc} /> </Row>
          <Row id = "result-description"> <p> {endLabels[1].descrip} </p> </Row>
          <Row id = "take-again" onclick = {this.props.reset}> <p> {endLabels[1].takeAgain} </p> </Row>
        </Container>
      )
    }
    if (this.props.currentQuestion === 7 && this.props.mostFrequent === "Sorcerer") {
      return (
        <Container fluid id = "end-screen"> 
          <Row id = "result"> <h1> {endLabels[2].result} </h1> </Row>
          <Row id = "result-picture"> <img src = {endLabels[2].imgSrc} /> </Row>
          <Row id = "result-description"> <p> {endLabels[2].descrip} </p> </Row>
          <Row id = "take-again" onclick = {this.props.reset}> <p> {endLabels[2].takeAgain} </p> </Row>
        </Container>
      )
    }
    if (this.props.currentQuestion === 7 && this.props.mostFrequent === "Bard") {
      return (
        <Container fluid id = "end-screen"> 
          <Row id = "result"> <h1> {endLabels[3].result} </h1> </Row>
          <Row id = "result-picture"> <img src = {endLabels[3].imgSrc} /> </Row>
          <Row id = "result-description"> <p> {endLabels[3].descrip} </p> </Row>
          <Row id = "take-again" onclick = {this.props.reset}> <p> {endLabels[3].takeAgain} </p> </Row>
        </Container>
      )
    }
  }
}

javascript-quiz-3.js (where the questions object is stored):

// HTML fill-in for each part of quiz //

export const startLabels = [
  {
    title: "Which RPG class are you?",
    imgSrc: "",
    descrip: "Pick the lock, or break the face?",
    startQuiz: "Start Quiz!",
  },
];

export const questions = [

  {
    question: "Pick your weapon!",
    imgSrc: "https://a.wattpad.com/cover/140215314-352-k843958.jpg",
    choiceA: {
      choice: "A stick I found on the ground.",
      type: 'Warrior',
    },
    choiceB: {
       choice: "I'll just steal if off someone.",
       type: 'Rogue',
    },
    choiceC: {
       choice: "Green stuff shooting out of my hands.",
       type: 'Sorcerer',
    },
    choiceD: {
       choice: "My silver tongue.",
       type: 'Bard',
   },
  }, 

  {
   question: "There's an orc guarding the chest you need to get to. Do you...",
   imgSrc: "https://a.wattpad.com/cover/140215314-352-k843958.jpg",
   choiceA: {
     choice: "Throw a rock to distract him and sneak in.",
     type: 'Rogue',
   },
   choiceB: {
      choice: "Smash their face in. And then the chest.",
      type: 'Warrior',
   },
   choiceC: {
      choice: "Convice the orc that you are their chief.",
      type: 'Bard',
   },
   choiceD: {
      choice: "Teleport the chest to you.",
      type: 'Sorcerer',
    },
  }, 

  {
    question: "There's a strange mist washign over you. How do you respond?",
    imgSrc: "https://a.wattpad.com/cover/140215314-352-k843958.jpg",
    choiceA: {
      choice: "Perceive what type of magic it is.",
      type: 'Sorcerer',
    },
    choiceB: {
       choice: "Use the mist to hide.",
       type: 'Rogue',
    },
    choiceC: {
       choice: "Ready your weapon and know you're going to use it.",
       type: 'Warrior',
    },
    choiceD: {
       choice: "Find the mist melancholic and beautiful. Get song ideas.",
       type: 'Bard',
   },
  }, 

   {
    question: "You think you got a bad deal from a shopkeeper on a ring you sold. Do you... ?",
    imgSrc: "https://a.wattpad.com/cover/140215314-352-k843958.jpg",
    choiceA: {
      choice: "Sneak back in when the shop is closed and take the ring.",
      type: 'Rogue',
    },
    choiceB: {
       choice: "Threaten the shop owner and note how you can smash their counter.",
       type: 'Warrior'
    },
    choiceC: {
       choice: "Cast an illusion in the shop that absorbs all metal objects in the shop until the ring is returned.",
       type: 'Sorcerer'
    },
    choiceD: {
       choice: "Charm the shopkeeper so they give you the ring back.",
       type: 'Bard',
   },
  }, 

  {
    question: "You get ambushed on the way to the city. You are clearly outnumbered.",
    imgSrc: "https://a.wattpad.com/cover/140215314-352-k843958.jpg",
    choiceA: {
      choice: "They may have numbers, but I have my axe.",
      type: 'Warrior',
    },
    choiceB: {
       choice: "Throw a smokebomb down and run to higher ground.",
       type: 'Rogue',
    },
    choiceC: {
       choice: "Create a ring of fire around you.",
       type: 'Sorcerer',
    },
    choiceD: {
       choice: "Play the ambush a song. Leave unaffected.",
       type: 'Bard',
   },
  }, 

  {
    question: "What does your ideal world look like?",
    imgSrc: "https://a.wattpad.com/cover/140215314-352-k843958.jpg",
    choiceA: {
      choice: "Everyone takes the time to learn.",
      type: 'Sorcerer'
    },
    choiceB: {
       choice: "Everyone would be honest and direct.",
       type: 'Warrior',
    },
    choiceC: {
       choice: "Endless beauty.",
       type: 'Bard',
    },
    choiceD: {
       choice: "Deeper pockets.",
       type: 'Rogue',
   },
  }, 

  {
    question: "The front door is locked. What do you do?",
    imgSrc: "https://a.wattpad.com/cover/140215314-352-k843958.jpg",
    choiceA: {
      choice: "Pick the lock of course!",
      type: 'Rogue',
    },
    choiceB: {
       choice: "Melt the lock.",
       type: 'Sorcerer',
    },
    choiceC: {
       choice: "It just needs more force!",
       type: 'Warrior',
    },
    choiceD: {
       choice: "Maybe find the key?",
       type: 'Bard',
   },
  }, 

  {
    question: "Your quest is done. What do you do to unwind?",
    imgSrc: "https://a.wattpad.com/cover/140215314-352-k843958.jpg",
    choiceA: {
      choice: "Flirt with the most beautiful people I can find.",
      type: 'Bard',
    },
    choiceB: {
       choice: "Explore the realm of each element.",
       type: 'Sorcerer',
    },
    choiceC: {
       choice: "Eat as much as I can!",
       type: 'Warrior',
    },
    choiceD: {
       choice: "Find a quiet corner to relax with my treasures.",
       type: 'Rogue',
   },
  },
];

export const endLabels = [
  {
  result: "Warrior",
  imgSrc: "",
  descrip: "No problem can't be solved with my axe.",
  takeAgain: "Take Again!"
  }, 


  {
  result: "Rogue",
  imgSrc: "",
  descrip: "The shadows are my natural home.",
  takeAgain: "Take Again!"
  }, 


  {
  result: "Sorcerer",
  imgSrc: "",
  descrip: "Life, to you, is a world of mysteries to be uncovered.",
  takeAgain: "Take Again!"
  }, 


  {
  result: "Bard",
  imgSrc: "",
  descrip: "The world is boring without a little beauty.",
  takeAgain: "Take Again!"
  }, 

];

navbar.js:

import React from 'react'; 
import {Navbar} from 'react-bootstrap'; 
import {Container} from 'react-bootstrap'; 
import {NavDropdown} from 'react-bootstrap'; 
import {Button} from 'react-bootstrap'; 
import {Link} from 'react-router-dom'; 

class TopNavbar extends React.Component {
    render() {
        return (
            <div>
                <Navbar expand = 'lg' bg = 'light' variant = 'light'>
                <Container fluid>
                    <NavDropdown title = "|||" id = "basic-nav-dropdown">
                        <NavDropdown.Item> <Link to = "/"> Quizzes </Link> </NavDropdown.Item> 
                        <NavDropdown.Item> <Link to = "/register"> Register </Link> </NavDropdown.Item> 
                        <NavDropdown.Item> <Link to = "/subscribe"> Subscribe </Link> </NavDropdown.Item> 
                    </NavDropdown>
                    <Link to = "/"> <img alt = 'placeholder'/> </Link>
                    <Link to = "/login"> <Button className = "login"> Login </Button></Link>
                </Container>
                </Navbar>
            </div>
        )
    }
} 

export default TopNavbar; 

Thank you to anyone who can help. I appreciate it.

3
  • 1
    Your score property in the state is initiated as an empty array. But in your reset() function, you set it to the number 0. You can't iterate over or use concat() on a number. Is the score state supposed to be a number or an array? Commented Apr 22, 2020 at 21:40
  • ohhh didn't even think about that function. Score is just supposed to be an empty array. I'll see if that helps. Commented Apr 22, 2020 at 21:42
  • @KristofferKarlsson That definitely helped with the errors. Thank you. Now my problem is that when the first answer is clicked on, the score array that displays in the console is empty, not filled with the with the first type associated with the answer. Subsequent questions answered fill the array up as intended. Do you have any suggestions? Commented Apr 22, 2020 at 21:53

1 Answer 1

1

Two issues I see:

  1. reset() you are setting score to 0, which will have no Array.prototype methods.
  2. Using the return value of Array.prototype.push. That returns the length of the new array as an int, not the entire array. If you're assigning this.state.score to the result of push anywhere, even just once, it will cause the issue as well: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/push
Sign up to request clarification or add additional context in comments.

2 Comments

That definitely helped with the errors. Thank you. Now my problem is that when the first answer is clicked on, the score array that displays in the console is empty, not filled with the with the first type associated with the answer. Subsequent questions answered fill the array up as intended. Do you have any suggestions?
It's hard to say without seeing the most recent code. Start testing some of your assumptions. Walk through your script with breakpoints or console.log and slowly narrow down what might be going wrong. Test all assumptions and think through what might cause the result you are seeing.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.