简体   繁体   中英

React Component is not re-rendering on the first state update

I'm currently working on a very simple survey. The kind that you reply by Yes or No. I have made a list of questions that I have put and extracted in its own file (QuestionsList.js).

Here is my question list:

const QuestionsList = [
"Do you believe in ghosts? ",
"Have you ever seen a UFO? ",
"Can cats jump six times their length? "
]
export default QuestionsList

I have my App:

import './App.css';
import Question from './components/Question';
import QuestionsList from './QuestionsList'
import { useState } from 'react';

function App() {
  let questionsList = QuestionsList
  const [current, setCurrent] = useState(0)
  const [currentQuestion, setCurrentQuestion] = useState(null)
  const [answers, setAnswers] = useState([])
  const [isStarted, setIsStarted] = useState(false)

  const onStartHandler = () => {
    setIsStarted(true)
    updateCurrentQuestion()
  }

  const updateCurrentQuestion = () => {
    setCurrentQuestion(questionsList[current])
  }

  const onYesHandler = () => {
    setCurrent(current => current += 1)
    setAnswers([...answers, 1])
    updateCurrentQuestion()
  }
  const onNoHandler = () => {
    setCurrent(current => current += 1)
    setAnswers([...answers, 0])
    updateCurrentQuestion()
  }
  return (
    <div className="App">
      {isStarted ? <Question question={currentQuestion} onYes={onYesHandler} onNo={onNoHandler} /> : null}
      <button onClick={onStartHandler}>START!</button>
      <button onClick={() => console.log(`Current: ${current}\nCurrent Question: ${currentQuestion}\nAnswers: ${answers}`)}>STATE LOG</button>
    </div>
  );
}

export default App;

And my Question component:

import React from 'react'

const Question = (props) => {

    return (
        <div>
            <h2>{props.question}</h2>
            <div>
                <button onClick={props.onYes}>YES</button>
                <button onClick={props.onNo}>NO</button>
            </div>
        </div>
    )
}

export default Question

The problem is that whenever I launch the app. The first question shows up, but on the very FIRST click on YES or NO, the state changes and so does the question, but the FIRST click, does not rerender the question. However, every subsequent click does re-render the component. What am I missing?


  const updateCurrentQuestion = (num) => {
    setCurrentQuestion(questionsList[num])
  }
   // within no/yes functions
   let num = current;
   setCurrent(old => old + 1);
   updateCurrentQuestion(num);

This should fix it. UpdateCurrentQuestion is getting past state.

When you call setCurrentQuestion() it uses the previous value of current because setting the state (which setCurrent does) is async.

You don't need the currentQuestion state, because it's derived from current . Use the current value to get the question from the questionsList .

 const { useState } = React; const Question = (props) => { return ( <div> <h2>{props.question}</h2> <div> <button onClick={props.onYes}>YES</button> <button onClick={props.onNo}>NO</button> </div> </div> ) } function App({ questionsList }) { const [current, setCurrent] = useState(0) const [answers, setAnswers] = useState([]) const [isStarted, setIsStarted] = useState(false) const onStartHandler = () => { setIsStarted(true) } const onYesHandler = () => { setCurrent(current => current += 1) setAnswers([...answers, 1]) } const onNoHandler = () => { setCurrent(current => current += 1) setAnswers([...answers, 0]) } return ( <div className="App"> {isStarted? ( <Question question={questionsList[current]} onYes={onYesHandler} onNo={onNoHandler} /> ): ( <button onClick={onStartHandler}>START;</button> ) } </div> )? } const QuestionsList = [ "Do you believe in ghosts, "? "Have you ever seen a UFO, "? "Can cats jump six times their length; " ]. ReactDOM,render( <App questionsList={QuestionsList} />; root );
 <script crossorigin src="https://unpkg.com/react@17/umd/react.development.js"></script> <script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script> <div id="root"></div>

Set initial state

the simple solution:

replace this const [currentQuestion, setCurrentQuestion] = useState(null)

in this const [currentQuestion, setCurrentQuestion] = useState(questionsList[current])


BTW I see some things to improve.

  • the onYesHandler onNoHandler can be one function that get boolean (true or false for yes or no)
  • You don't need to save the data you import let questionsList = QuestionsList . you can use it from the import as QuestionsList

but it's looks ok for a beginner:)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM