如何编写依赖于反应道具的计时器间隔?

How to write a timer interval that is dependent on a prop in react?

我正在为我的网站创建一个简单的弹出窗口 window 组件,我想每隔一秒左右定期收集任何新消息或传入消息以实现实时消息传递功能。我目前正在为我的项目使用 React。

我的主要问题是,我只想在名为“active”的属性设置为 True 时收集消息,但我不知道该怎么做。

这是我的网页模型,希望对您有所帮助。 It is of a mockup of the webpage and has three entries, one with an open messaging window.

我有多个条目可以打开消息 window,但我已经传递了其他道具,因此一次只能打开一个消息 window。因此,有可能在组件的一个祖先中有一个间隔/计时器的东西,但我想让这个弹出窗口 window 通过自己调用 API.[=13 更加自给自足=]

我试过使用 setInterval 函数,该函数在消息 window 组件内的 useEffect 函数中初始化:

    let my_interval = useRef() // ???? Don't think this is the solution

    const timerHandler = useCallback(() => {
        // Some call to my RestAPI here.
        console.log("Timer called")
    }, active)

    // If the "active" prop turns true, the interval should be running. Else, clear the interval if it exists.
    // Here, the trouble is the interval handle "my_interval" is changing so I can't clear the original interval.
    useEffect(() => {
        if (active) my_interval = setInterval(timerHandler, 1000);
        else if (!active) clearInterval(my_interval)
    }, [active])

然而,正如评论指出的那样,我实际上无法清除间隔,因为我找不到使间隔句柄 my_interval 足够稳定的方法(useRef 不起作用)

我也尝试过将超时函数与递归调用一起使用,只要 active 为真,它就会继续调用自身,但我认为我有一个类似的问题,因为函数本身正在改变,所以 递归调用有点坏了。

    // Only do action when active is set to true. This doesn't work, when active changes to false the timer still runs.
    const timeoutHandler = useCallback(() => { 
        if (active) console.log("Timer called")
        setTimeout(timeoutHandler, 1000);
    }, [active])

    // Set a timeout on render, regardless of "active"
    useEffect(() => {
        setTimeout(timeoutHandler, 1000);
    }, [])

任何帮助或不同的策略将不胜感激,我真的希望我的 Messenger 弹出窗口能够自给自足地调用我的 RestAPI,但目前看来这几乎是不可能的。

useRef是一个对象,其API的see docs

const intervalRef = useRef();

// handle on active state change
useEffect(() => {
  if (active) {
    intervalRef.current = setInterval(() => {
      console.log("Timer called");
    }, 1000);
  } else {
    clearInterval(intervalRef.current);
  }
}, [active]);

// handle unmount
useEffect(() => {
  const intervalId = intervalRef.current;
  return () => {
    clearInterval(intervalId);
  };
}, []);