严格模式下 React 自定义 useCountDown 钩子中的奇怪错误

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

我编写了一个自定义的 React

useCountDown
钩子来倒数一个数字(秒)直到零。计数由按钮
start()
侦听器中附加的
onClick
方法触发。

没有

<React.StrictMode>
也能正常工作,但在严格模式下就不行了。

import { useCallback, useEffect, useState } from "react";

export default function useCountDown(countdownSeconds = 10) {
  const [timing, setTiming] = useState(false);
  const [seconds, setSeconds] = useState(countdownSeconds);

  const reset = useCallback(() => {
    setTiming(true);
    setSeconds(countdownSeconds);
  }, [countdownSeconds]);

  const start = useCallback(() => setTiming(true), []);

  useEffect(() => {
    let timer: number | undefined;

    function countdown() {
      setSeconds((preSecond) => {
        if (preSecond <= 1) {
          setTiming(false);
          return countdownSeconds;
        } else {
          timer = setTimeout(countdown, 1000);
          return preSecond - 1;
        }
      });
    }
    if (timing) {
      timer = setTimeout(countdown, 1000);
    }
    return () => {
      clearTimeout(timer);
    };
  }, [timing, countdownSeconds]);

  return {
    start,
    seconds,
    timing,
    reset,
  };
}

严格模式下,计数快速减零,不间隔(1s)且永不停止计数。但是,当我删除

<React.StrictMode>
时,它运行得很好。

我发现

countdown()
执行了太多次,没有间隔。代码有什么问题吗?我该如何解决这个问题?

代码沙盒

我知道另一种方法将在严格模式下工作,如下所示。但我试图找出上述方法出了什么问题。

useEffect(() => {
    let timer: NodeJS.Timeout
    if (timing) {
      timer = setTimeout(() => {
        if (seconds > 0) {
          setSeconds(seconds - 1)
        } else {
          onCountdownEndRef.current?.()
          setSeconds(countdownSeconds)
          setTiming(false)
        }
      }, 1000)
    }
    return () => clearTimeout(timer)
  }, [seconds, timing, countdownSeconds])

======编辑=====

即使我将

setTiming(false)
从更新程序功能中移出,它也不起作用。

  useEffect(() => {
    seconds === 0 && setTiming(false);
  }, [seconds]);

  useEffect(() => {
    let timer: number | undefined;

    function countdown() {
      setSeconds((preSecond) => {
        if (preSecond <= 1) {
          return countdownSeconds;
        } else {
          timer = setTimeout(countdown, 1000);
          return preSecond - 1;
        }
      });
    }
    if (timing) {
      timer = setTimeout(countdown, 1000);
    }
    return () => {
      clearTimeout(timer);
    };
  }, [timing, countdownSeconds]);
reactjs react-hooks countdown
1个回答
0
投票

使用refs不是更方便吗?

export default function useCountDown(countdownSeconds = 10) {
  const seconds = useRef(countdownSeconds);
  const interval = useRef();
  const [, update] = useReducer((x) => !x, 0);

  const reset = useCallback(() => {
    seconds.current = countdownSeconds;
    update();
  }, [countdownSeconds]);

  const start = useCallback(() => {
    interval.current = setInterval(() => {
      if (seconds.current < 1) {
        clearInterval(interval.current);
        return;
      }
      seconds.current -= 1;
      update();
    }, 1000);
  }, []);

  return {
    start,
    seconds: seconds.current,
    reset
  };
}
© www.soinside.com 2019 - 2024. All rights reserved.