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.