如何在 CSS 转换完成后调用函数 - React

问题描述 投票:0回答:1

我有一个带有卡片网格的 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);
  }
javascript reactjs css-transitions settimeout
1个回答
1
投票

快速回答:您可以使用

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()
运行时,它都会重置计时器(因为它会取消旧的超时并创建一个新的超时)。根据需要修改。

在 CodeSandbox 观看演示

© www.soinside.com 2019 - 2024. All rights reserved.