反应钩子和setInterval [英] react hooks and setInterval

查看:56
本文介绍了反应钩子和setInterval的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

除了在后台保留时钟以使用react钩子在轮播中实现自动下一步(几秒钟后)之外,还有其他选择吗?

Is there any alternative to just keeping a "clock" in the background to implement auto-next (after a few seconds) in carousel using react hooks?

下面的自定义react钩子实现了轮播的状态,该轮播支持手动(下一个,上一个,重置,重置)和自动(启动,停止)方法来更改轮播的当前(活动)索引。

The custom react hook below implements a state for a carousel that supports manual (next, prev, reset) and automatic (start, stop) methods for changing the carousel's current (active) index.

const useCarousel = (items = []) => {
  const [current, setCurrent] = useState(
    items && items.length > 0 ? 0 : undefined
  );

  const [auto, setAuto] = useState(false);

  const next = () => setCurrent((current + 1) % items.length);
  const prev = () => setCurrent(current ? current - 1 : items.length - 1);
  const reset = () => setCurrent(0);
  const start = _ => setAuto(true);
  const stop = _ => setAuto(false);


useEffect(() => {
    const interval = setInterval(_ => {
      if (auto) {
        next();
      } else {
        // do nothing
      }
    }, 3000);
    return _ => clearInterval(interval);
  });

  return {
    current,
    next,
    prev,
    reset,
    start,
    stop
  };
};


推荐答案

setInterval之间有区别 setTimeout ,您可能不希望在重新渲染组件时始终重新启动计时器而丢失。 此小提琴显示了当其他代码也在运行时,两者之间的漂移差异。 (在较旧的浏览器/机器上,就像我最初回答这个问题时一样,您甚至无需模拟大型计算就可以看到仅几秒钟后开始出现明显的漂移。)

There are differences between setInterval and setTimeout that you may not want to lose by always restarting your timer when the component re-renders. This fiddle shows the difference in drift between the two when other code is also running. (On older browsers/machines—like from when I originally answered this question—you don't even need to simulate a large calculation to see a significant drift begin to occur after only a few seconds.)

现在参考您的答案,Marco,使用 setInterval 完全丢失是因为无条件的效果会在每次重新渲染组件时处理并重新运行。因此,在您的第一个示例中,使用 current 依赖项会导致该效果在每次 current 更改(每次运行间隔)。第二个功能执行相同的操作,但实际上每次状态更改时(导致重新渲染),这都可能导致某些意外行为。起作用的唯一原因是因为 next()导致状态更改。

Referring now to your answer, Marco, the use of setInterval is totally lost because effects without conditions dispose and re-run every time the component re-renders. So in your first example, the use of the current dependency causes that effect to dispose and re-run every time the current changes (every time the interval runs). The second one does the same thing, but actually every time any state changes (causing a re-render), which could lead to some unexpected behavior. The only reason that one works is because next() causes a state change.

考虑到您可能不关心的事实精确计时最干净,使用 current 来简单地使用 setTimeout auto vars作为依赖项。因此,要重新陈述部分答案,请执行以下操作:

Considering the fact that you are probably not concerned with exact timing, is is cleanest to use setTimeout in a simple fashion, using the current and auto vars as dependencies. So to re-state part of your answer, do this:

useEffect(
  () => {
    if (!auto) return;
    const interval = setTimeout(_ => {
      next();
    }, autoInterval);
    return _ => clearTimeout(interval);
  },
  [auto, current]
);

一般来说,对于那些刚刚阅读此答案并想要做一个简单计时器的人来说,这里的版本不既不考虑OP的原始代码,也不考虑它们独立启动和停止计时器的方式:

Generically, for those just reading this answer and want a way to do a simple timer, here is a version that doesn't take into account the OP's original code, nor their need for a way to start and stop the timer independently:

const [counter, setCounter] = useState(0);
useEffect(
  () => {
    const id= setTimeout(() => {
      setCounter(counter + 1);
    }, 1000);
    return () => {
      clearTimer(id);
    };
  },
  [counter],
);

但是,考虑到 setTimeout这一事实,您可能想知道如何使用更精确的间隔的偏移量可以超过 setInterval 。同样,这是一种不使用OP代码的通用方法:

However, you may be wondering how to use a more exact interval, given the fact that setTimeout can drift more than setInterval. Here is one method, again, generic without using the OP's code:

const [counter, setCounter] = useState(30);
const r = useRef(null);
r.current = { counter, setCounter };
useEffect(
  () => {
    const id = setInterval(() => {
      r.current.setCounter(r.current.counter + 1);
    }, 1000);
    return () => {
      clearInterval(id);
    };
  },
  [], // empty dependency array
);

这是怎么回事?好吧,要使 setInterval 的回调始终引用当前可接受的 setCounter 版本,我们需要一些可变的状态。 React通过 useRef 给了我们。 useRef 函数将返回具有 current 属性的对象。然后我们可以将该属性(每次重新渲染组件时都会发生)设置为当前版本的 counter setCounter

What is happening here? Well, to get setInterval's callback to always refer to the currently acceptable version of setCounter we need some mutable state. React gives us this with useRef. The useRef function will return an object that has a current property. We can then set that property (which will happen every time the component re-renders) to the current versions of counter and setCounter.

然后,为了防止间隔在每个渲染器上消失,我们在 useEffect 。就我而言。

Then, to keep the interval from being disposed of on each render, we add an empty dependency array as the second argument to useEffect. In my case. The interval will still be cleared when the component is unmounted.


注意:我以前喜欢使用 [ once] 作为我的依赖项数组,以指示我仅将此效果设置为一次。当时的可读性很好,但出于两个原因,我不再使用它。首先,近来对钩子有了更广泛的了解,并且我们在各处都看到了空数组。第二,它与非常流行的钩子规则相冲突。 linter对依赖项数组中的内容非常严格。

Note: I used to like using ["once"] as my dependency array to indicate that I am forcing this effect to be set up only once. It was nice for readability at the time, but I no longer use it for two reasons. First, hooks are more widely understood these days and we have seen the empty array all over the place. Second, it clashes with the very popular "rule of hooks" linter which is quite strict about what goes in the dependency array.

因此,将我们所知道的应用于OP的原始问题,可以使用 setInterval 进行像这样不太可能漂移的幻灯片显示:

So applying what we know to the OP's original question, you could use setInterval for a less-likely-to-drift slideshow like this:

// ... OP's implementation code including `autoInterval`,
// `auto`, and `next` goes above here ...

const r = useRef(null);
r.current = { next };
useEffect(
  () => {
    if (!auto) return;
    const id = setInterval(() => {
      r.current.next();
    }, autoInterval);
    return () => {
      clearInterval(id);
    };
  },
  [auto],
);

这篇关于反应钩子和setInterval的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆