I'm trying to make a countdown timer using React-Native, React Hooks, MomentJS and (setTimeout/setInterval). Whatever approach I try to use, it fails. The problem is that ponent is never re-rendered.
I tried to follow the official React Hooks documentation, a few articles on Medium, for example The Iceberg of React Hooks but nothing works.
One possibility is that it needs deep clone of the MomentJS object, but it's an inefficient approach I guess.
This is one of the reproducible examples that I've tried.
const Timer = () => {
const [time, setTime] = useState(moment.duration(30, 'seconds'))
const intervalRef = useRef()
useEffect(() => {
intervalRef.current = setTimeout(() => {
setTime(prevTime => prevTime.subtract(1, 'second'))
}, 1000)
return () => {
clearInterval(intervalRef.current)
intervalRef.current = null
}
})
return (
<View>
{time.asSeconds()}
</View>
)
I'm trying to make a countdown timer using React-Native, React Hooks, MomentJS and (setTimeout/setInterval). Whatever approach I try to use, it fails. The problem is that ponent is never re-rendered.
I tried to follow the official React Hooks documentation, a few articles on Medium, for example The Iceberg of React Hooks but nothing works.
One possibility is that it needs deep clone of the MomentJS object, but it's an inefficient approach I guess.
This is one of the reproducible examples that I've tried.
const Timer = () => {
const [time, setTime] = useState(moment.duration(30, 'seconds'))
const intervalRef = useRef()
useEffect(() => {
intervalRef.current = setTimeout(() => {
setTime(prevTime => prevTime.subtract(1, 'second'))
}, 1000)
return () => {
clearInterval(intervalRef.current)
intervalRef.current = null
}
})
return (
<View>
{time.asSeconds()}
</View>
)
Share
Improve this question
asked Aug 30, 2019 at 14:48
Honza SedloňHonza Sedloň
3741 gold badge11 silver badges28 bronze badges
4
-
Mutating the object isn't what you should with
set*()
(ad absurdum - you do not even need to callsetTime()
in that case). To avoid re-rendering when state/properties do not change is one of those basic optimization that makes React easy to use. To create a newduration
object is a negligible overhead and you can quickly do it usingclone()
. Alternatively you might consider to usenumber
instead ofmoment.Duration
and to create a new object when rendering. Also note that you do not needintervalRef
, you can saveconst ref = setTimeout(...); return () => clearInterval(ref);
– Adriano Repetti Commented Aug 30, 2019 at 15:06 - It's odd. I always thought that in order to not-rerender a ponent when the state did changed but the object was identical you had to implement it as a pure ponent – apokryfos Commented Aug 30, 2019 at 15:33
- the state hasn't actually changed though, the reference to the object is the same – Will Jenkins Commented Aug 30, 2019 at 15:45
-
@WillJenkins in non-functional ponents calling
setState
always rerenders. Thats why I was confused, though now that I think about it set state probably always clones the state – apokryfos Commented Aug 30, 2019 at 15:49
2 Answers
Reset to default 6You're correct, it isn't re-rendering because your moment object is the same (but mutated) on every tick. You can easily get it working by adding .clone()
in your setTime
updater:
const Timer = () => {
const [time, setTime] = useState(moment.duration(30, "seconds"));
const intervalRef = useRef();
useEffect(() => {
intervalRef.current = setTimeout(() => {
setTime(prevTime => prevTime.clone().subtract(1, 'second'))
}, 1000)
return () => {
clearInterval(intervalRef.current)
intervalRef.current = null
}
})
return <div>{time.asSeconds()}</div>;
};
Working sandbox here: https://codesandbox.io/s/gifted-euler-e8xg5
One possibility is that it needs deep clone of the MomentJS object, but it's an inefficient approach I guess.
Yes exactly. React doesn't rerender if the current and the previous state equal. You could just store the seconds in the state.
And you don't need that ref.
const Timer = () => {
const [time, setTime] = useState(30 /*s*/)
useEffect(() => {
const timeout = setTimeout(() => {
setTime(prevTime => prevTime - 1);
}, 1000)
return () => clearTimeout(timeout);
}, [time])
return (
<View>
{time}
</View>
);
发布者:admin,转转请注明出处:http://www.yc00.com/questions/1745351888a4623882.html
评论列表(0条)