为什么我的计时器钩子不更新它的内部状态?

时间:2021-05-15 17:51:39

标签: react-hooks

由于某种原因,我的计时器在修改输入字段后没有更新它的内部计时器状态。这是我的页面和状态的初始状态。

Initial State

这是我将输入从 10 秒修改为 8 秒后我的屏幕和状态的样子。请注意,计时器状态不会更新

Timer state not updating

这是我的锻炼页面代码:

function WorkoutPage(props: any) {

const DEFAULT_SECONDS_BETWEEN_REPS: number = 10
const [secondsBetweenRepsSetting, setSecondsBetweenRepsSetting] = useState(DEFAULT_SECONDS_BETWEEN_REPS)
const {secondsLeft, isRunning, start, stop} = useTimer({
        duration: secondsBetweenRepsSetting,
        onExpire: () => sayRandomExerciseName(),
        onTick: () => handleTick(),
    })
const onTimeBetweenRepsChange = (event: any) => {
        const secondsBetweenRepsSettingString = event.target.value;
        const secondsBetweenRepsSettingInt = parseInt(secondsBetweenRepsSettingString)
        setSecondsBetweenRepsSetting(secondsBetweenRepsSettingInt)
    }

return <React.Fragment>
<input type="number" name="secondsBetweenRepsSetting" value={secondsBetweenRepsSetting} onChange={onTimeBetweenRepsChange}/>
</React.Fragment>

}

这是我的 useTimer 类:

import { useState } from 'react';
import Validate from "../utils/Validate";
import useInterval from "./useInterval";

export default function useTimer({ duration: timerDuration, onExpire, onTick}) {
  const [secondsLeft, setSecondsLeft] = useState(timerDuration)
  const [isRunning, setIsRunning] = useState(false)

  function start() {
    setIsRunning(true)
  }
  function stop() {
    setIsRunning(false)
  }

  function handleExpire() {
    Validate.onExpire(onExpire) && onExpire();
  }

  useInterval(() => {
    const secondsMinusOne = secondsLeft - 1;
    setSecondsLeft(secondsMinusOne)
    if(secondsMinusOne <= 0) {
      setSecondsLeft(timerDuration) // Reset timer automatically
      handleExpire()
    } else {
      Validate.onTick(onTick) && onTick();
    }
  }, isRunning ? 1000 : null)

  return {secondsLeft, isRunning, start, stop, }
}

我的完整代码库在这里,以防有人感兴趣:https://github.com/kamilski81/bdt-coach

1 个答案:

答案 0 :(得分:1)

这是您期望的事件序列:

  • 用户更改输入
  • 更改处理程序触发并使用新值调用 setSecondsBetweenRepsSetting
  • 组件使用 secondsBetweenRepsSetting 的新值重新渲染
  • useTimer 使用新值的 duration 属性调用
  • secondsLeft 挂钩中的 useTimer 状态更改为新的 duration<-- 哎呀!这不会发生

为什么最后一项没有发生?因为在 useTimer 实现中,您唯一使用持续时间的地方是 secondsLeft初始值。使用新的持续时间值再次调用钩子不会改变 secondsLeft 状态,这是设计使然。

我的建议是将 setSecondsLeft 包含在 useTimer 挂钩的返回值中,以便您可以覆盖计时器中剩余的时间。然后,您可以直接在输入更改处理程序中使用 setSecondsLeft

const { secondsLeft, setSecondsLeft, isRunning, start, stop } = useTimer({
  duration: secondsBetweenRepsSetting,
  onExpire: () => sayRandomExerciseName(),
  onTick: () => handleTick(),
});

const onTimeBetweenRepsChange = (event: any) => {
  const secondsBetweenRepsSettingString = event.target.value;
  const secondsBetweenRepsSettingInt = parseInt(
    secondsBetweenRepsSettingString
  );
  setSecondsBetweenRepsSetting(secondsBetweenRepsSettingInt);
  setSecondsLeft(secondsBetweenRepsSettingInt);
};
相关问题