javascript - 为什么这个 React 状态数组不填充对象数据?

标签 javascript arrays reactjs this

我正在做一个测验,作为更大的测验网站的一部分。基本思想是,当用户单击包含问题对象生成的文本的答案时,对象中关联的“类型”字符串将添加到状态“分数”数组中。最后,我使用 for 循环来确定最常选择哪个答案字符串,并给出结果。

我的问题是分数数组无法正确填充这些字符串,而且我不完全确定发生了什么。我知道您不应该尝试直接更改状态,而应该在 setState 之外进行转换,或者使用迭代。

当我尝试使用迭代时 - 又名类似这样的东西:

    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 无法迭代。

好的。因此,我尝试使用 .concat 在 setState 之外操作它(也尝试过推送,但直接操作状态,这会导致问题)。

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 是 stateScore.concat() 不是一个函数。我不知道必须如此,也不知道如何纠正该错误。

长话短说,我试图用外部对象的值填充分数状态数组,但我做不到。我还制作了更多函数,但为了简单起见,我想将它们排除在这些主要示例之外。这是完整的代码,以防我错过了什么。 (我知道函数 B-D 正在使用推送。我打算稍后从函数 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!"
  }, 

];

导航栏.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. reset() 您将 score 设置为 0,它将没有 Array.prototype 方法。
  2. 使用Array.prototype.push的返回值。它以 int 形式返回新数组的长度,而不是整个数组。如果您在任何地方将 this.state.score 分配给 push 的结果,即使只是一次,也会导致问题:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/push

关于javascript - 为什么这个 React 状态数组不填充对象数据?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61375458/

相关文章:

javascript - 在 Node.js 网络中使用 pipe()

javascript - 移动另一个 div 的 CSS 动画

javascript - 为什么当一个组件被包装在高阶组件中时,新组件没有原始组件的任何静态方法?

javascript - 如何防止 Bootstrap 使字体变粗?

javascript - 将多个对象作为值添加到另一个对象中的键

java - 多维数组中的 ArrayIndexOutOfBoundsException

javascript - 数组添加额外的方括号

javascript - 显示不带括号的对象数组,但元素(包括逗号)必须保留

javascript - 使用 React 内联样式设置第三方元素的样式

javascript - 如何删除 chrome 图标添加到主屏幕提示