javascript - 使用 useState 的函数之间状态不持久

标签 javascript reactjs use-state

我正在练习 React useState hooks 来进行一个测验,每个问题有 10 秒的计时器。

目前,我可以从 API 获取测验问题,将其设置为状态并呈现它们。如果用户单击答案,问题将从状态数组中删除,状态秒数重置为 10,然后呈现下一个问题。

当状态中的问题数组中没有任何内容时,我试图让计时器清除。当我在 startTimer 函数中使用 console.log(questions) 时,它是一个空数组,尽管相同的 console.log 显示了 userAnswer 函数中的数据?我在这里缺少什么?

我删除了引用的随机播放函数以节省空间

function App() {

  // State for trivia questions, time left, right/wrong count
  const [questions, setQuestions] = useState([])
  const [seconds, setSeconds] = useState(10);
  const [correct, setCorrect] = useState(0);
  const [incorrect, setIncorrect] = useState(0);

  // Get data, set questions to state, start timer
  const getQuestions = async () => {
    let trivia = await axios.get("https://opentdb.com/api.php?amount=10&category=9&difficulty=easy&type=multiple")
    trivia = trivia.data.results
    trivia.forEach(result => {
      result.answers = shuffle([...result.incorrect_answers, result.correct_answer])
    })
    setQuestions(trivia)
    startTimer()
  }

  const startTimer = () => {

    // Empty array here, but data at beginning of userAnswer
    console.log(questions)

    const interval = setInterval(() => {

      // If less than 1 second, increment incorrect, splice current question from state, clear interval and start timer back at 10
      setSeconds(time => {
        if (time < 1) {
          setIncorrect(wrong => wrong + 1)
          setQuestions(q => {
            q.splice(0,1)
            return q;
          })
          clearInterval(interval);
          startTimer()
          return 10;
        }
        // I want this to hold the question data, but it is not
        if (questions.length === 0) {
          console.log("test")
        }
        // Else decrement seconds 
        return time - 1;
      });
    }, 1000);
  }

  // If answer is right, increment correct, else increment incorrect
  // Splice current question from const questions
  const userAnswer = (index) => {

    // Shows array of questions here
    console.log(questions)

    if (questions[0].answers[index] === questions[0].correct_answer) {
      setCorrect(correct => correct + 1)
    } else {
      setIncorrect(incorrect => incorrect + 1)
    }
    setQuestions(q => {
      q.splice(0,1);
      return q;
    })
    
    // Shows same array here despite splice in setQuestions above
    console.log(questions)
    setSeconds(10);
  }

  return (
    <div>
      <button onClick={() => getQuestions()}></button>
      {questions.length > 0 &&
        <div>
          <Question question={questions[0].question} />
          {questions[0].answers.map((answer, index) => (
            <Answer
              key={index}
              answer={answer}
              onClick={() => userAnswer(index)} />
          ))}
        </div>
      }
      <p>{seconds}</p>
      <p>Correct: {correct}</p>
      <p>Incorrect: {incorrect}</p>
    </div>

  )
}

export default App;

最佳答案

App 的每次渲染都将创建其内部函数和变量的新绑定(bind)(例如 questionsstartTimer)。

当单击按钮并且 getQuestions 运行时,getQuestions 然后调用 startTimer 函数。此时,startTimer 函数会关闭单击按钮时定义的 questions 变量 - 但它是空的那一点。在间隔内,虽然函数已重新渲染,但间隔回调仍然引用旧闭包中的问题

如果 questions 数组已填充。

另一个问题是 splice 不应该与 React 一起使用,因为它会改变现有数组 - 请改用非改变方法,例如 slice.

尝试如下操作:

// Get data, set questions to state, start timer
const getQuestions = async () => {
  let trivia = await axios.get("https://opentdb.com/api.php?amount=10&category=9&difficulty=easy&type=multiple")
  trivia = trivia.data.results
  trivia.forEach(result => {
    result.answers = shuffle([...result.incorrect_answers, result.correct_answer])
  })
  setQuestions(trivia);
  setSeconds(10);
}
useLayoutEffect(() => {
  if (!questions.length) return;
  // Run "timer" if the quiz is active:
  const timeoutId = setTimeout(() => {
    setSeconds(time => time - 1);
  }, 1000);
  return () => clearTimeout(timeoutId);
});
// If quiz is active and time has run out, mark this question as wrong
// and go to next question:
if (questions.length && time < 1) {
  setIncorrect(wrong => wrong + 1)
  setQuestions(q => q.slice(1));
  setSeconds(10);
}
if ((correct || incorrect) && !questions.length) {
  // Quiz done
  console.log("test")
}

关于javascript - 使用 useState 的函数之间状态不持久,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63607859/

相关文章:

javascript - 为什么我不能在 React Native 组件中使用 connect ?

reactjs - 仅当 React 中的状态发生变化时才更改变量值

javascript - 为什么我不能将 <Switch> 分离到不同的模块?

reactjs - 无法接收 id 并使用 useParams 使用它。 react 中页面显示空白

reactjs - useState 钩子(Hook)一次只能设置一个对象,并将同一个数组的另一个对象返回到初始状态

javascript - 使用计算变量与 useState/useEffect 的结果

javascript - 如何在不卡住浏览器的情况下使用 javascript 中的 setTimeOut 函数处理大循环?

JavaScript - 如果禁用复选框,则使用 jQuery 添加类

javascript - 如何将 jQuery 函数应用于所有具有相同类的元素。?

javascript - 使用 javascript 绘制页眉/页脚包括和 document.write