Почему этот массив состояний React не заполняется данными объекта? - PullRequest
0 голосов
/ 23 апреля 2020

Я делаю викторину как часть более крупного сайта викторины. Основная идея c состоит в том, что, когда пользователь нажимает на ответ с текстом, сгенерированным объектом вопросов, связанная с ним строка «тип» в объекте добавляется в массив «оценка» состояния. В конце я использую для l oop, чтобы определить, какая строка ответа была выбрана наиболее часто, давая результат.

Моя проблема в том, что массив показателей не будет правильно заполняться этими строками , и я не совсем уверен, что происходит. Я знаю, что вы не должны пытаться изменить состояние напрямую, и должны либо выполнять преобразование вне setState, либо использовать итерацию.

Когда я пытаюсь использовать итерацию - AKA примерно так:

    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;
  }

Я получаю TypeError, который говорит, что this.state.score не может быть повторен.

Хорошо. Поэтому я пытаюсь манипулировать им вне setState с помощью .concat (также пробовал pu sh, но это напрямую манипулирует состоянием, что вызывает проблемы).

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;
  }

Тогда это говорит о том, что TypeError - это то состояниеScoreScore .concat () не является функцией. Я не знал, что это должно было быть, или как исправить эту ошибку.

Короче говоря, я пытаюсь заполнить массив состояния счета значениями из внешнего объекта, и я не могу. Есть и другие функции, которые я сделал, но я хотел опустить их в этих основных примерах ради простоты. Вот полный код на случай, если я что-то пропустил. (Я знаю, что функция BD использует pu sh. Я собирался скопировать код позже из функции A)

quiz3. js ( Само приложение React ):

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 (где хранится объект вопросов) :

// 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; 

Спасибо всем, кто может помочь. Я ценю это.

1 Ответ

1 голос
/ 23 апреля 2020

Я вижу две проблемы:

  1. reset() вы устанавливаете score на 0, что не будет иметь методов Array.prototype.
  2. Использование возвращаемого значения Array.prototype.push. Это возвращает длину нового массива в виде целого, а не всего массива. Если вы присвоите this.state.score результату push в любом месте, хотя бы один раз, это также вызовет проблему: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/push
...