I am working on countdown app but when i start it it decrease by one and after that values of hour, minute and second shows as undefined, undefined and nan.
const [time, setTime] = useState({ 'hour': 0, 'minute': 0, 'second': 0 });
const savedTime = useRef(null);
const changeTime = (e) => {
setTime({ ...time, [e.target.name]: e.target.value });
};
let updatedHour = time.hour, updatedMinute = time.minute, updatedSecond = time.second;
const onStart = () => {
savedTime.current = setInterval(()=> tick(), 1000);
};
useEffect(() => {
onStart()
return () => clearInterval(savedTime.current)
}, [time])
this is my time state which have hour, min and sec.
function tick(){
if (updatedHour === 0 && updatedMinute === 0 && updatedSecond === 0) {
onReset()
} else if (updatedMinute === 0 && updatedSecond === 0) {
setTime({updatedHour: updatedHour - 1, updatedMinute: 59, updatedSecond: 59})
} else if (updatedSecond === 0 ) {
setTime({updatedMinute: updatedMinute - 1, updatedSecond: 59})
} else {
setTime({updatedHour: updatedHour, updatedMinute: updatedMinute,updatedSecond: updatedSecond - 1})
}
};
this is tick function which will decrease time by 1 second.
<div>
<span>{updatedHour} :</span>
<span>{updatedMinute} :</span>
<span>{updatedSecond}</span>
</div>
<div>
<input onChange={changeTime} name="hour" value={updatedHour} />
<input onChange={changeTime} name="minute" value={updatedMinute} />
<input onChange={changeTime} name="second" value={updatedSecond}/>
</div>
here is inputs that will show data.
CodePudding user response:
Issue is in your tick() function you are setting
setTime({updatedHour: updatedHour - 1, updatedMinute: 59, updatedSecond: 59})
instead of
setTime({hour: updatedHour - 1, minute: 59, second: 59})
here is a fully working example https://codesandbox.io/s/fervent-taussig-kf0x2?file=/src/App.js
import { useState, useEffect } from "react";
export default function App() {
const [time, setTime] = useState({ hour: 0, minute: 0, second: 0 });
const changeTime = (e) => {
setTime({ ...time, [e.target.name]: e.target.value });
};
let updatedHour = time.hour,
updatedMinute = time.minute,
updatedSecond = time.second;
useEffect(() => {
let timer = setInterval(() => tick(), 1000);
return () => clearInterval(timer);
}, []);
const tick = () => {
if (updatedHour === 0 && updatedMinute === 0 && updatedSecond === 0) {
// onReset();
return;
} else if (updatedMinute === 0 && updatedSecond === 0) {
setTime({
hour: updatedHour - 1,
minute: 59,
second: 59
});
} else if (updatedSecond === 0) {
setTime({ minute: updatedMinute - 1, second: 59 });
} else {
setTime({
hour: updatedHour,
minute: updatedMinute,
second: updatedSecond - 1
});
}
};
return (
<>
<div>
<span>{updatedHour} :</span>
<span>{updatedMinute} :</span>
<span>{updatedSecond}</span>
</div>
<div>
<input onChange={changeTime} name="hour" value={updatedHour} />
<input onChange={changeTime} name="minute" value={updatedMinute} />
<input onChange={changeTime} name="second" value={updatedSecond} />
</div>
</>
);
}
CodePudding user response:
This happens because in setTime
you set the fields updatedHour
, updatedMinute
and updatedSecond
, instead of the original hour
, minute
, second
. Try it this way:
function tick() {
if (updatedHour === 0 && updatedMinute === 0 && updatedSecond === 0) {
onReset();
} else if (updatedMinute === 0 && updatedSecond === 0) {
setTime((time) => ({
hour: time.hour - 1,
minute: 59,
second: 59
}));
} else if (updatedSecond === 0) {
setTime((time) => ({
...time,
minute: time.minute - 1,
second: 59
}));
} else {
setTime((time) => ({
...time,
second: time.second - 1
}));
}
}
I used the callback function in setTime
because it is considered a recommended practice. If you wish, you can use the normal setTime
call, example:
setTime({
...time,
minute: time.minute - 1,
second: 59
});
And do not forget that setTime
does not merge the passed state with the previous one, but replaces it.