带有反应钩子的计时器在重新渲染后不会更新



我正在创建一个带有React钩子的计时器。这是一个用于测验的简单组件。每个问题都有一个定义的持续时间,所以计时器应该在这个持续时间开始,并开始一秒一秒地减少。我的问题是,组件做了应该做的事情,但当我转到下一个问题时,状态不会初始化为props中传递的持续时间,而是继续计数器。。。

const Timer = ({ duration }) => {
const [counter, setCounter] = useState(duration);
const timer = useRef();
debugger;
console.log("counter: " + counter);
const setTimer = () => {
if (counter <= duration) {
setCounter(counter - 1);
}
};
// Similar to componentDidMount and componentDidUpdate:
useEffect(() => {
timer.current = setTimeout(() => setTimer(), 1000);
return () => {
if (timer.current) {
console.log("ClearInterval in Timer");
clearTimeout(timer.current);
}
};
}, [counter]);
return (
<div>
<p>time left {counter} seconds</p>
</div>
);
};
export default Timer;

我正在从卡组件呈现计时器组件:

import React from "react";
import QuizButton from "../QuizButton/QuizButton";
import Timer from "../Timer/Timer";
import "./styles.css";
const QuizQuestion = ({ question, responses, checkAnswerFn, duration }) => (
<article className='card'>
<header>
<Timer duration={duration} />
</header>
<div>
<p>{question}</p>
</div>
<footer>
{responses.map((response, i) => {
return (
<QuizButton key={i} onClick={checkAnswerFn(response)}>
{response}
</QuizButton>
);
})}
</footer>
</article>
);
export default QuizQuestion;

有人知道为什么在问题重新呈现后,状态没有初始化为持续时间吗?

看起来有点复杂,可以告诉你你的代码是如何以及为什么没有按预期工作的,但这里有一个今天刚刚完成的工作示例,希望它能帮助你指明正确的方向:

const [counter, setCounter] = useState(15000);
...

useEffect(() => {
const myInterval = () => {
if (counter > 1000) {
setCounter(state => state - 1000)
} else if (counter !== 0){
setCounter(0);
clearInterval(interval);
}
}
const interval = setInterval(myInterval, 1000);
return () => {
clearInterval(interval)
}
}, [counter]);
...
console.log(counter); // 15000, 14000, 13000, ...

setTimer函数中的条件不允许counter初始化。让您的函数递减计数器,直到并且除非计数器值大于0,否则用duration初始化它。用以下函数替换您的函数,它将帮助您将计时器初始化回持续时间。

const setTimer = () => {
setCounter(counter > 0  ? counter - 1 : duration);
};

更新2-添加了问题数组和基于问题的计时器数据

更新1-我认为它现在符合你的愿望,成为

总结-我已经提升了一些州。

因此,编辑后的答案是:由于我们需要向Child Timer组件重新发送新的道具数据,而触发器是基于子组件的,我已经使用子组件的单击进行了演示,因此我们将一个函数从父Question组件传递给子组件,该函数将通过道具在onClick侦听器上调用。因此,该函数会更新父对象(Question.js(的状态,重新呈现子对象(Timer.js(并传递新的prop值,无论我们将其设置为新值。与基于计时器的更改不同,我们还更改为基于数据的触发器,如更改Question data以调用useEffect。

我希望这个解释能起作用,并解决您的用例。

//Question.js    
import React, { useState, useEffect } from "react";
import Timer from "./Timer";
import "./styles.css";
const Question = () => {
const questionPool = [
{ number: 1, text: "lorem", timer: 6 },
{ number: 2, text: "lorem", timer: 10 },
{ number: 3, text: "lorem", timer: 20 },
{ number: 4, text: "lorem", timer: 3 },
{ number: 5, text: "lorem", timer: 12 }
];
const [countDown, setCountDown] = useState(questionPool[0].timer);
const [currentQuestion, setCurrentQuestion] = useState(
questionPool[0].number
);

const resetCount = (newQuestion) => {
newQuestion < questionPool.length + 1
? setCurrentQuestion(newQuestion)
: setCurrentQuestion(1);
};
useEffect(() => {
if (questionPool[currentQuestion]) {
setCountDown(questionPool[currentQuestion].timer);
} else {
setCountDown(questionPool[0].timer);
}
}, [currentQuestion]);
return (
<>
<Timer
duration={countDown}
resetCountDown={resetCount}
questionNumber={currentQuestion}
/>
</>
);
};
export default Question;

//Timer.js
import React, { useEffect, useState } from "react";
import "./styles.css";
const Timer = ({ duration, resetCountDown, questionNumber}) => {
const [counter, setCounter] = useState(duration);
let interval = null;
useEffect(() => {
const myInterval = () => {
if (counter > 1) {
setCounter((state) => state - 1);
} else if (counter !== 0) {
setCounter(0);
clearInterval(interval);
}
};
interval = setInterval(myInterval, 1000);
return () => {
clearInterval(interval);
};
}, [counter]);
useEffect(() => {
if (!interval) {
setCounter(duration);
}
}, [questionNumber]);
return (
<div>
<p>
time left {counter} seconds For Question: {questionNumber}
</p>
<button type="button" onClick={() => resetCountDown(questionNumber + 1)}>
next
</button>
</div>
);
};
export default Timer;

CodeSandbox从@Adolfo Onrubia扩展以匹配您的用例

检查这是否有帮助。

*********以下已弃用*******************

如果你仍然想动态添加定时器组件,我认为你需要在道具中推送一个完整的react组件,并让正在推送新组件的组件重新渲染,这样子组件就会自己渲染,并且可以出现带有新组件的新问题。

简而言之,

  • 您的问题数组[{OBJECT WITH QUESTION DETAILS}]位于您的主应用程序中
  • 您的useState类似于:

const[currentQuestion,setCurrentQuestion]=useState(QuestionArray[0](;

每当按下下一个按钮时。你做

setCurrentQeusion(QuestionArray[NewIndex](;

因此,您的主组件应用程序组件的状态在currentQuestionuseEffect上重新呈现,并将其传递给Timer.js/child组件,使其以新的超时持续时间重新呈现。

检查props.children也可能是值得的,所以与其只是作为常规道具发送,不如像模板一样传递。

<Template>
{currentQuestion}
</Template>

多亏了Fauzul和Adolfo,我终于解决了我的问题。

我将问题的id传递给计时器的道具,并使用useEffect钩子,每次id更改时,计数器都会更新为持续时间。。。

测验问题.js

import React from "react";
import PropTypes from "prop-types";
import QuizButton from "../QuizButton/QuizButton";
import Timer from "../Timer/Timer";
import "./styles.css";
const QuizQuestion = ({ question, responses, checkAnswerFn, duration, id }) => (
<article className='card'>
<header>
<Timer duration={duration} id={id} />
</header>
<div>
<p>{question}</p>
</div>
<footer>
{responses.map((response, i) => {
return (
<QuizButton key={i} onClick={checkAnswerFn(response)}>
{response}
</QuizButton>
);
})}
</footer>
</article>
);
QuizQuestion.propTypes = {
question: PropTypes.string.isRequired,
responses: PropTypes.array.isRequired,
checkAnswerFn: PropTypes.func.isRequired,
};
export default QuizQuestion;

Timer.js

import React, { useState, useEffect, useRef } from "react";
import PropTypes from "prop-types";
const Timer = ({ duration, id }) => {
const [counter, setCounter] = useState(duration);
const timer = useRef();
const setTimer = () => {
if (counter > 0) {
setCounter(counter - 1);
}
};
// Similar to componentDidMount and componentDidUpdate:
useEffect(() => {
timer.current = setTimeout(() => setTimer(), 1000);
return () => {
if (timer.current) {
clearTimeout(timer.current);
}
};
}, [counter]);
useEffect(() => {
setCounter(duration);
}, [id]);
return (
<div>
<p>time left {counter} seconds</p>
</div>
);
};
Timer.propTypes = {
duration: PropTypes.number.isRequired,
id: PropTypes.number.isRequired,
};
export default Timer;

相关内容

  • 没有找到相关文章

最新更新