javascript - 我怎样才能让我的每个测验问题及其在不同页面中的选择通过 axios 使用react

标签 javascript reactjs axios

我对 React 很陌生。我正在准备测验。我的问题和他们的选择都在同一页上。我想要的是将每个问题及其选择放在不同的页面中。单击按钮选择一个选项后,我想传递到下一页的问题。我怎样才能做到这一点?

import React from "react";
import axios from "axios";

function Question(props) {
  this.state = { questionNumber: 0 };
  let style = {
    margin: "5px"
  };
  clickHandler = () => {};

  return (
    <div className="Question">
      <h4>{props.question}</h4>
      <ul>
        {props.choices.map((c, i) => (
          <button style={style} clickHandler={this.clickHandler}>
            {c.choice}
          </button>
        ))}
      </ul>
    </div>
  );
}

class QuizApp extends React.Component {
  constructor(props) {
    super(props);
    this.state = { questions: [], choices: [] };
  }

  componentDidMount() {
    axios
      .get(
        "https://private-anon-c06008d89c-quizmasters.apiary-mock.com/questions"
      )
      .then(response => {
        this.setState({ questions: response.data });
      });
  }

  render() {
    return <div>{this.state.questions.map(q => new Question(q))}</div>;
  }
}

最佳答案

页面并不完全不同,但您可以使用问题和答案数组来给出不同页面的错觉,但所有页面都在同一组件中。一次只显示一个元素。例如,假设您的获取请求中的 response.data 如下:

[
  {
    question: 'What is question 1?',
    option1: 'Option 1',
    option2: 'Option 2',
    option3: 'Option 3'
  },
  {
    question: 'What is question 2?',
    option1: 'Option 1',
    option2: 'Option 2',
    option3: 'Option 3'
  },
  {
    question: 'What is question 3?',
    option1: 'Option 1',
    option2: 'Option 2',
    option3: 'Option 3'
  },
]

您希望一次显示该数组的一个索引,因此您需要另一个名为 currentQuestionIndex 的状态属性。从 0 开始,以便它可以从该数组的第一个问题开始。

您还需要在某个地方存储您的答案,因此,我们将添加一个 answers 状态属性。这两个都应该在构造函数中定义,现在看起来像这样:

  constructor (props, context) {
    super(props, context)
    this.state = {
      currentQuestionIndex: 0,
      questions: [],
      answers: []
    }
  }

需要在 componentDidMount 中设置 questions 状态属性,以便代码中的该部分到目前为止看起来不错。

我们设置了渲染部分,使其仅显示 currentQuestionIndex 上的任何问题。 我还将添加“下一步”按钮,以便您可以导航到下一个问题。

它应该看起来像这样:

  render() {
    const { questions, currentQuestionIndex, answers } = this.state

    const { question, option1, option2, option3} = questions[currentQuestionIndex]

    return (<div>
        <h4>{question}</h4>

          <label>
            <input type='radio'
              value={option1}/>
            {option1}
          </label>
        <br/>

          <label>
            <input type='radio'
              value={option2}/>
            {option2}
          </label>
          <br/>
          <label>
            <input type='radio'
              value={option3}/>
            {option3}
          </label>
        <hr/>
        <button>Next</button>
      </div>);
  }

酷!现在我们需要通过添加 checked 属性并添加一个处理程序来使这个单选输入按钮实际工作。这就是我们的 answers 状态发挥作用的地方。 checked 属性是一个 bool 值,因此我们希望只要单选按钮的当前值与 answers 数组中的索引相同,就可以检查该单选按钮。这就是我们的处理程序的样子:

onChangeOption (value) {
    const { currentQuestionIndex } = this.state
    let answers = [...this.state.answers]
    answers[currentQuestionIndex] = value

    this.setState({answers}) 
  }

让我们更新渲染函数以合并 checked 属性和 onChangeOption 函数:

  render() {
    const { questions, currentQuestionIndex, answers } = this.state

    const { question, option1, option2, option3} = questions[currentQuestionIndex]

    return (<div>
        <h1>Question {currentQuestionIndex + 1}</h1>
        <h4>{question}</h4>

          <label>
            <input type='radio'
              checked={answers[currentQuestionIndex] === option1}
              value={option1}
              onChange={(evt) => this.onChangeOption(evt.target.value)}/>
            {option1}
          </label>
        <br/>

          <label>
            <input type='radio'
              checked={answers[currentQuestionIndex] === option2}
              value={option2}
              onChange={(evt) => this.onChangeOption(evt.target.value)}/>
            {option2}
          </label>
          <br/>
          <label>
            <input type='radio'
              checked={answers[currentQuestionIndex] === option3}
              value={option3}
              onChange={(evt) => this.onChangeOption(evt.target.value)}/>
            {option3}
          </label>
        <hr/>
        <button>Next</button>
      </div>);
  }

这应该可以处理我们在该页面中的问题处理。但是我们应该如何更改页面呢?让我们处理“下一步”按钮,好吗?

如前所述,定义显示的问题是通过使用 currentQuestionIndex 的索引来定义的,因此单击下一个按钮后,我们可以增加该索引。这是 onClick 处理程序:

  handleNext () {
     let incrementCurrentQuestionIndex = this.state.currentQuestionIndex + 1
     this.setState({currentQuestionIndex: incrementCurrentQuestionIndex})
  }

下一个按钮应该是这样的:

<button onClick={() => this.handleNext()}>Next</button>

最后,让我们添加一些最后的修饰,以便这个小应用程序能够正常运行:

1- 由于您仅在 componentDidMount 生命周期方法上收到问题,因此您需要在问题仍在加载时添加占位符。因此,在 render() 方法中,在渲染问题之前,您需要检查 questions 状态是否已填写。如下所示:

render() {
    const { questions, currentQuestionIndex, answers } = this.state

    // Will be rendered before you grab all your questions
    if (!questions.length) {
      return <div> Loading questions...</div>
    }

    // Do the rest...
  }

2- 接下来,一旦阵列结束,您可能需要禁用“下一步”按钮。当然,在您的实际应用程序中,您会以不同的方式处理它,但您需要确保没有传递问题数组的大小。对于此示例,我们只需禁用“下一步”按钮,因此它将如下所示:

 <button disabled={currentQuestionIndex === questions.length - 1} onClick={() => this.handleNext()}>Next</button>

更新:这有一个尴尬的流程,因此对于这个示例,当您没有问题时,最好显示不同的页面。为此,请在 render() 函数上添加一个条件,如果 currentQuestionIndex 较大或已达到问题数组的大小,则显示测验已结束的条件。所以在你的问题被提出之前是这样的:

if (currentQuestionIndex >= questions.length) {
      return (
        <div>
          <h3>End of the Quiz!</h3>
        </div>
      )
    }

示例应用程序也已相应更新。

tl;dr:利用您收到的一系列问题。只显示您当前所在索引的问题和选项。当您想移至下一个时,只需单击按钮即可增加当前索引。

这是功能齐全的示例应用程序:

//Simulating data coming from axios

const questionsArray = [
  {
    question: 'What is question 1?',
    option1: 'Option 1',
    option2: 'Option 2',
    option3: 'Option 3'
  },
  {
    question: 'What is question 2?',
    option1: 'Option 1',
    option2: 'Option 2',
    option3: 'Option 3'
  },
  {
    question: 'What is question 3?',
    option1: 'Option 1',
    option2: 'Option 2',
    option3: 'Option 3'
  },
]

class App extends React.Component {

  constructor (props, context) {
    super(props, context)
    this.state = {
      currentQuestionIndex: 0,
      questions: [],
      answers: []
    }
  }
  
  componentDidMount() {
    // Do your axios call and set the questions state.
    // For the sake of simplicity,I'll be using my array.
    this.setState({questions: questionsArray})
    
  }
  
  handleNext () {
     let incrementCurrentQuestionIndex = this.state.currentQuestionIndex + 1
     this.setState({currentQuestionIndex: incrementCurrentQuestionIndex})
  }
  
  onChangeOption (value) {
    const { currentQuestionIndex } = this.state
    let answers = [...this.state.answers]
    answers[currentQuestionIndex] = value
    
    this.setState({answers}) 
  }
  
  render() {
    const { questions, currentQuestionIndex, answers } = this.state

    if (!questions.length) {
      return <div> Loading questions...</div>
    }

if (currentQuestionIndex >= questions.length) {
  return (
    <div>
      <h3>End of the Quiz!</h3>
    </div>
  )
}
    
    const { question, option1, option2, option3} = questions[currentQuestionIndex]
  
    return (<div>
        <h1>Question {currentQuestionIndex + 1}</h1>
        <h4>{question}</h4>
        
          <label>
            <input type='radio'
              checked={answers[currentQuestionIndex] === option1}
              value={option1}
              onChange={(evt) => this.onChangeOption(evt.target.value)}/>
            {option1}
          </label>
        <br/>
        
          <label>
            <input type='radio'
              checked={answers[currentQuestionIndex] === option2}
              value={option2}
              onChange={(evt) => this.onChangeOption(evt.target.value)}/>
            {option2}
          </label>
          <br/>
          <label>
            <input type='radio'
              checked={answers[currentQuestionIndex] === option3}
              value={option3}
              onChange={(evt) => this.onChangeOption(evt.target.value)}/>
            {option3}
          </label>
        <hr/>
        <button onClick={() => this.handleNext()}>Next</button>
      </div>);
  }

}

ReactDOM.render(
    <App />,
    document.getElementById('app')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>

关于javascript - 我怎样才能让我的每个测验问题及其在不同页面中的选择通过 axios 使用react,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51338597/

相关文章:

javascript - Ember.js:如何创建仅执行代码的路由

Javascript/jQuery 在单个对象中转换 DOM

javascript - 使用谷歌脚本对谷歌表格中的数据进行排序(使用自定义排序顺序)

node.js - 尽管已赋值,但变量在函数作用域外未定义?

ruby-on-rails - Rails 5.2 使用 Axios 请求返回 406

javascript - .each 函数中的 "this"未指向 .each 的调用者

javascript - React reducer 可以工作,但是一旦浏览网页就变得不确定

reactjs - 如何使用 Redux Provider 包装导航堆栈(createSwitchNavigator)?

javascript - 在Vue中使用Axios Api发布数据

rest - Axios 发布未经授权的错误但 curl 有效