admin管理员组文章数量:1422504
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>
);
本文标签:
版权声明:本文标题:javascript - Why does updating state using React Hooks, MomentJS object and intervaltimeout not trigger re-render? - Stack Overf 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1745351887a2654817.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论