简体   繁体   English

为什么使用React Hooks,MomentJS对象和时间间隔/超时更新状态不会触发重新渲染?

[英]Why does updating state using React Hooks, MomentJS object and interval/timeout not trigger re-render?

I'm trying to make a countdown timer using React-Native, React Hooks, MomentJS and (setTimeout/setInterval). 我正在尝试使用React-Native,React Hooks,MomentJS和(setTimeout / setInterval)制作一个倒数计时器。 Whatever approach I try to use, it fails. 无论我尝试使用哪种方法,它都会失败。 The problem is that component 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. 我尝试遵循React Hooks的官方文档,在Medium上写了几篇文章,例如React Hooks的Iceberg,但没有任何效果。

One possibility is that it needs deep clone of the MomentJS object, but it's an inefficient approach I guess. 一种可能是它需要MomentJS对象的深层克隆,但是我猜这是一种低效的方法。

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>
  )

One possibility is that it needs deep clone of the MomentJS object, but it's an inefficient approach I guess. 一种可能是它需要MomentJS对象的深层克隆,但是我猜这是一种低效的方法。

Yes exactly. 对,就是这样。 React doesn't rerender if the current and the previous state equal. 如果当前状态和先前状态相等,则React不会重新渲染。 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>
 );

You'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: 您可以通过在setTime更新程序中添加.clone()轻松地使其工作:

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 此处的工作沙箱: https//codesandbox.io/s/gifted-euler-e8xg5

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM