在间隔内使用时更新状态时出现问题
Problem updating state when it is being used in an interval
我正在构建一个 React Native 应用程序并尝试创建一个计时器。计时器使用 setInterval 在前台正常运行,并更新保存秒数的状态位,并每隔一秒减少一次。当应用程序在后台运行时,日期将被捕获并存储。当活动再次激活时,我试图计算两个日期之间的差异,然后更新状态以反映差异,因此在用户看来,计时器在后台 运行。
我 运行 遇到计时器为 运行 时状态未更新的问题。
const home = () => {
const WORK_SECONDS = 1500; //1500
const SHORT_REST_SECONDS = 300; //300
const LONG_REST_SECONDS = 900; //900
const ACTIVE_COLOR = '#009688';
const PAUSED_COLOR = '#00695C';
const ADD_TIME = 60;
const NOTIFICATION_TITLE = "Time's up!";
const NOTIFICATION_BODY = "Finished!";
const [timer, setTimer] = useState({seconds: WORK_SECONDS, timeCap: WORK_SECONDS });
const [isActive, setIsActive] = useState(false);
const [timerColor, setTimerColor] = useState(ACTIVE_COLOR);
const [sessionCycle, setSessionCycle] = useState([]);
let time = 0;
const toggle = () => {
if (sessionCycle.length == 8) setSessionCycle([]);
setIsActive(!isActive);
setTimerColor((timerColor == ACTIVE_COLOR && timer.seconds != timer.timeCap) ? PAUSED_COLOR : ACTIVE_COLOR);
}
const reset = () => {
setTimer({...timer, seconds: timer.timeCap});
setIsActive(false);
}
useEffect(() => {
AppState.addEventListener('change', handleChange);
return () => {
AppState.removeEventListener('change', handleChange);
}
}, []);
const handleChange = (newState) => {
if (newState === "active") {
let resumeTime = moment(new Date());
let exitTime = moment(time);
let duration = moment.duration(resumeTime.diff(exitTime, 'seconds'));
let diff = duration.asSeconds() * 1000;
console.log("Attempting to substract - " + diff);
console.log(timer.seconds + " from appstate");
setTimer({...timer, seconds: timer.seconds - diff})
console.log(timer.seconds + " after subtraction");
}
else if (newState == "background") {
time = new Date();
}
}
useEffect(() => {
let interval = null;
if (isActive) {
if (timer.seconds == 0) {
//sendNotification();
// TODO: Add +1 to pomo total after finishing work session
updateSessionCycle("forward");
clearInterval(interval);
toggle();
}
interval = setInterval(() => {
console.log(timer.seconds);
setTimer({...timer, seconds: timer.seconds - 1});
}, 1000);
} else if (!isActive && timer.seconds !== 0) {
clearInterval(interval);
}
return () => clearInterval(interval);
}, [isActive, timer.seconds]);
const addTime = () => {
if (timer.seconds > timer.timeCap - 60) {
setTimer({...timer, seconds: timer.timeCap})
} else {
setTimer({...timer, seconds: timer.seconds + ADD_TIME});
}
}
return (
<View style={styles.chart}>
<ProgressCircle
percent={ (timer.seconds / timer.timeCap) * 100 }
radius={Math.ceil(Dimensions.get('window').height / 6)}
borderWidth={Math.ceil(Dimensions.get('window').height / 30)}
color={timerColor}
shadowColor="#212121"
bgColor="#303030"
>
<Text style={{ color: '#FFF', fontSize: 50 }}>{moment("2015-01-01").startOf('day').seconds(timer.seconds).format('m:ss')}</Text>
<Text style={{ color: '#9E9E9E', fontSize: 20 }}>{(timer.timeCap == WORK_SECONDS) ? "Work Session" : "Break"}</Text>
</ProgressCircle>
</View>
);
}
我怀疑您的问题是您可能在调用 setTimer
时使用了陈旧状态。在您的 useEffect
中,您正在创建一个 setInterval
函数,它在您的函数闭包中引用 timer
。每次调用 setInterval
函数时,您将获得相同的 timer
变量,因此调用:
setTimer({...timer, seconds: timer.seconds - 1});
...将连续将 seconds
设置为比创建间隔时 timer.seconds
的值少 1 秒。
幸运的是有一个简单的解决方法:
setTimer(timer => ({...timer, seconds: timer.seconds - 1}));
这将在每次执行更新之前获取最新的 timer
状态。
我正在构建一个 React Native 应用程序并尝试创建一个计时器。计时器使用 setInterval 在前台正常运行,并更新保存秒数的状态位,并每隔一秒减少一次。当应用程序在后台运行时,日期将被捕获并存储。当活动再次激活时,我试图计算两个日期之间的差异,然后更新状态以反映差异,因此在用户看来,计时器在后台 运行。
我 运行 遇到计时器为 运行 时状态未更新的问题。
const home = () => {
const WORK_SECONDS = 1500; //1500
const SHORT_REST_SECONDS = 300; //300
const LONG_REST_SECONDS = 900; //900
const ACTIVE_COLOR = '#009688';
const PAUSED_COLOR = '#00695C';
const ADD_TIME = 60;
const NOTIFICATION_TITLE = "Time's up!";
const NOTIFICATION_BODY = "Finished!";
const [timer, setTimer] = useState({seconds: WORK_SECONDS, timeCap: WORK_SECONDS });
const [isActive, setIsActive] = useState(false);
const [timerColor, setTimerColor] = useState(ACTIVE_COLOR);
const [sessionCycle, setSessionCycle] = useState([]);
let time = 0;
const toggle = () => {
if (sessionCycle.length == 8) setSessionCycle([]);
setIsActive(!isActive);
setTimerColor((timerColor == ACTIVE_COLOR && timer.seconds != timer.timeCap) ? PAUSED_COLOR : ACTIVE_COLOR);
}
const reset = () => {
setTimer({...timer, seconds: timer.timeCap});
setIsActive(false);
}
useEffect(() => {
AppState.addEventListener('change', handleChange);
return () => {
AppState.removeEventListener('change', handleChange);
}
}, []);
const handleChange = (newState) => {
if (newState === "active") {
let resumeTime = moment(new Date());
let exitTime = moment(time);
let duration = moment.duration(resumeTime.diff(exitTime, 'seconds'));
let diff = duration.asSeconds() * 1000;
console.log("Attempting to substract - " + diff);
console.log(timer.seconds + " from appstate");
setTimer({...timer, seconds: timer.seconds - diff})
console.log(timer.seconds + " after subtraction");
}
else if (newState == "background") {
time = new Date();
}
}
useEffect(() => {
let interval = null;
if (isActive) {
if (timer.seconds == 0) {
//sendNotification();
// TODO: Add +1 to pomo total after finishing work session
updateSessionCycle("forward");
clearInterval(interval);
toggle();
}
interval = setInterval(() => {
console.log(timer.seconds);
setTimer({...timer, seconds: timer.seconds - 1});
}, 1000);
} else if (!isActive && timer.seconds !== 0) {
clearInterval(interval);
}
return () => clearInterval(interval);
}, [isActive, timer.seconds]);
const addTime = () => {
if (timer.seconds > timer.timeCap - 60) {
setTimer({...timer, seconds: timer.timeCap})
} else {
setTimer({...timer, seconds: timer.seconds + ADD_TIME});
}
}
return (
<View style={styles.chart}>
<ProgressCircle
percent={ (timer.seconds / timer.timeCap) * 100 }
radius={Math.ceil(Dimensions.get('window').height / 6)}
borderWidth={Math.ceil(Dimensions.get('window').height / 30)}
color={timerColor}
shadowColor="#212121"
bgColor="#303030"
>
<Text style={{ color: '#FFF', fontSize: 50 }}>{moment("2015-01-01").startOf('day').seconds(timer.seconds).format('m:ss')}</Text>
<Text style={{ color: '#9E9E9E', fontSize: 20 }}>{(timer.timeCap == WORK_SECONDS) ? "Work Session" : "Break"}</Text>
</ProgressCircle>
</View>
);
}
我怀疑您的问题是您可能在调用 setTimer
时使用了陈旧状态。在您的 useEffect
中,您正在创建一个 setInterval
函数,它在您的函数闭包中引用 timer
。每次调用 setInterval
函数时,您将获得相同的 timer
变量,因此调用:
setTimer({...timer, seconds: timer.seconds - 1});
...将连续将 seconds
设置为比创建间隔时 timer.seconds
的值少 1 秒。
幸运的是有一个简单的解决方法:
setTimer(timer => ({...timer, seconds: timer.seconds - 1}));
这将在每次执行更新之前获取最新的 timer
状态。