react-hooks - 为什么我的计时器挂钩不更新它的内部状态?
问题描述
由于某种原因,在我修改输入字段后,我的计时器没有更新它的内部计时器状态。这是我的页面和状态的初始状态。
这是我将输入从 10 秒修改为 8 秒后的屏幕和状态。请注意,计时器状态不会更新
这是我的锻炼页面代码:
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
解决方案
以下是您期望的事件顺序:
- 用户更改输入
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);
};
推荐阅读
- python - 导入 OAuth2Authentication ImportError:没有名为“oauth2_provider.ext”的模块
- java - 如果您对 CompletableFuture.runAsync() 进行了太多调用,会发生什么?
- google-cloud-platform - Cloud Run:项目中缺少结算帐号
- linux - 如何使这个 AWK 命令不区分大小写?
- php - 在不知道每个键的情况下对嵌套的关联数组进行排序
- python-3.x - 如何通过 pylint 传递 optionparser 参数?
- elasticsearch - Serilog 输出到文件和弹性搜索的最佳方法
- c# - C#:范围的 DataAnnotation 不适用于前导“+”号
- regex - 将所有 http/www 重定向到 https://www
- javascript - 历史推送与路由不匹配。仅更改 URL 和默认值以捕获所有路由