我有一个带有卡片网格的 React 应用程序。我添加了一些 javascript 和 CSS 过渡来为卡片翻转设置动画。点击后,卡片会翻转到背面,等待2秒后会自动翻转回正面。 这是我的代码。
isQuestionAnswered
和 isCorrect
是从父组件传递下来的 props。
const [isFlipped, setIsFlipped] = useState(false);
const [trigger, setTrigger] = useState(0);
useEffect(() => {
if (isFlipped || !trigger) return;
updateScore(points);
}, [isFlipped]);
function flipCard() {
if (!isQuestionAnswered || !isCorrect) return;
setIsFlipped(true);
setTimeout(() => {
setTrigger((prevState) => prevState + 1);
setIsFlipped(false);
}, 2000)
}
我想在转换完成后运行
updateScore()
函数(另一个道具)(因此在卡片翻转回前面之后)。但是,当 updateScore()
状态设置回 false 后,isFlipped
函数将立即运行。然后,在翻转转换(需要 0.8 秒完成)发生之前,updateScore()
函数将 isQuestionAnswered
设置为 false。如何才能在转换完成后运行 updateScore()
函数?
我尝试过不使用
useEffect
钩子和 trigger
状态,而只是在 updateScore()
函数内的超时时间内运行 flipCard
。我还尝试使用 setTimeout
内的 updateScore()
在 0.8 秒延迟(转换发生所需的时间)后将 isQuestionAnswered
设置为 true,但这些方法都不起作用。
这是当前的
updateScore()
功能:
function updateScore(points) {
setScore((prevState) => prevState + points);
setIsQuestionAnswered(false);
}
onTransitionEnd
或 onAnimationEnd
,请注意我们如何使用 React 名称。
首先我们创建一个新状态来保存 timeoutId。这是一个很好的做法,以防万一用户离开我们想要取消超时的页面。
const [savedTimeoutId, setSavedTimeoutId] = useState(0)
然后我们修改
flipCard
函数以在开始之前清除旧的超时,然后它将通过将其返回给调度函数来设置 savedTimeoutId
。阅读更多内容根据之前的状态更新状态 - React。
我们还修改了 useEffect 以取消超时,如果我们在翻转回前面之前卸载组件。
const [savedTimeoutId, setSavedTimeoutId] = useState(0)
function flipCard() {
// if (!isQuestionAnswered || !isCorrect) return;
setIsFlipped(true);
setSavedTimeoutId((prev) => {
// if we already have a timeout, cancel it
if (prev !== 0) clearTimeout(prev);
// start a new timeout and set it to savedTimeoutId
return setTimeout(() => {
setIsFlipped(false)
}, 2000)
})
}
useEffect(() => {
// if we move away from this page before we flip back to the
// front, properly dispose of the setTimeout
return () => {
clearTimeout(savedTimeoutId);
}
}, [savedTimeoutId])
最后在我们翻转到前面时附加一些功能
onTransitionEnd
到updateScore
return (
<>
<span>Score</span>
<span> {score}</span>
<br />
<button
className="test"
onClick={() => {
// setIsFlipped((prev) => !prev);
flipCard(true);
}}
>
Flip Card
</button>
<div
className={isFlipped ? "flipped card" : "card"}
onTransitionEnd={() => {
// Only update score after flipping to front
if (!isFlipped) {
// can easily swap for updateScore function or anything else
setScore(score + 1);
}
}}
>
Card Front
</div>
<div className={!isFlipped ? "flipped card" : "card"}>Card Back</div>
</>
);
请注意,每次
flipCard()
运行时,它都会重置计时器(因为它会取消旧的超时并创建一个新的超时)。根据需要修改。