1

I'm trying to make a single question quiz app with react on codepen. I'm using an api to get a question, 3 incorrect answers and a correct answer to the question and adding it to the state. I am having an issue while validating the answer. After clicking on one of the 4 options, I want to conditionally render a div with the result. But the issue is that after the result is displayed, another radio input is getting rendered.

const appRoot = document.getElementById('app');

class App extends React.Component{
  state={
    question:'',
    correct:'',
    incorrect:[],
    result: null
  }

componentDidMount(){
  axios.get('https://opentdb.com/api.php?amount=1&type=multiple')
  .then(res => {
    const data = res.data.results[0];
    const q = data.question;
    const correct = data.correct_answer;
    const incorrect = data.incorrect_answers;
    this.setState({question:q,correct:correct,incorrect:incorrect})
  })
}

evaluate(selected){
  if(selected === this.state.correct){
    this.setState({result: 'Correct!'})
  } else {
    this.setState({result:`Wrong! Correct Answer is ${this.state.correct}`})
  }
}

render(){
  const random = Math.floor(Math.random() * 3);
  const options = this.state.incorrect;
  options.splice(random, 0, this.state.correct);
  const choices = options.map((option,i) => (
    <div>
      <input type='radio' 
     name='option' 
     value={option} 
     key={i} onChange={() => this.evaluate(option)}/>
      <label for='option'>{option}</label>
    </div>
  ) )
  return(
    <div>
      <div>{this.state.question}</div>
      <div>{choices}</div>
      <div>{this.state.result}</div>
    </div>
  )
}
}

ReactDOM.render(<App />, appRoot)
<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>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<div id='app'></div>

1 Answer 1

1

This seems like an honest mistake (one I am guilty of consistently):

In your render method you mutate your array:

const options = this.state.incorrect;
options.splice(random, 0, this.state.correct);

I suspect what you want is:

const options = this.state.incorrect.slice(); //create a COPY of the array
options.splice(random, 0, this.state.correct);

React is basically a state machine that looks for changes to the state and intelligently updates parts that depend on that state. But it is just JavaScript. By directly splicing the state object you're changing the state in the render method. React doesn't know you changed the state and as such, you're ending up with unexpected behaviors--particularly as render is called frequently.

I've copied your snippet so you can see a working example.

const appRoot = document.getElementById('app');

class App extends React.Component{
  state={
    question:'',
    correct:'',
    incorrect:[],
    result: null
  }

componentDidMount(){
  axios.get('https://opentdb.com/api.php?amount=1&type=multiple')
  .then(res => {
    const data = res.data.results[0];
    const q = data.question;
    const correct = data.correct_answer;
    const incorrect = data.incorrect_answers;
    this.setState({question:q,correct:correct,incorrect:incorrect})
  })
}

evaluate(selected){
  if(selected === this.state.correct){
    this.setState({result: 'Correct!'})
  } else {
    this.setState({result:`Wrong! Correct Answer is ${this.state.correct}`})
  }
}

render(){
  const random = Math.floor(Math.random() * 3);
  const options = this.state.incorrect.slice();
  options.splice(random, 0, this.state.correct);
  const choices = options.map((option,i) => (
    <div>
      <input type='radio' 
     name='option' 
     value={option} 
     key={i} onChange={() => this.evaluate(option)}/>
      <label for='option'>{option}</label>
    </div>
  ) )
  return(
    <div>
      <div>{this.state.question}</div>
      <div>{choices}</div>
      <div>{this.state.result}</div>
    </div>
  )
}
}

ReactDOM.render(<App />, appRoot)
<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>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<div id='app'></div>

Sign up to request clarification or add additional context in comments.

Comments

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.