如何在 React 中每秒递增一个状态变量

How to increment a state variable every second in React

我的代码中有一个名为“url”的组件的图像列表。我还有一个名为 currImageIndex 的状态变量。我想要发生的是每次渲染这个组件时,我想启动一个计时器。这个计时器每秒 运行s,我想增加我的状态变量 currImageIndex。当 currImageIndex + 1 大于或等于 url 长度时,我想将 currImageIndex 重置为零。总体目标是通过返回 <img src={url[currImageIndex]}> 来模拟图像的自动幻灯片放映。但是,我在增加状态变量时遇到问题。我按照我在此处找到的一些代码进行操作,但我无法弄清楚如何使增量工作。这是我的代码。

const { url } = props;
  const [currImageIndex, setCurrImageIndex] = useState(0);
  console.log(url)
  const increment = () => {
    console.log(url.length)
    console.log(currImageIndex)
    if (currImageIndex + 1 < url.length) {
      setCurrImageIndex((oldCount) => oldCount + 1)
    }
    else {
      setCurrImageIndex(0)
    }
  }
  useEffect(() => {
    const id = setInterval(increment, 1000);

    return () => clearInterval(id);
  }, []);

  return (
    <div className={styles.container}>
      <div className={styles.header}>Preview of GIF:</div>
      <img src={url[currImageIndex]} alt="GIF Preview" className={styles.image} />
      <div>{currImageIndex}</div>
    </div>
  );

当我 运行 使用大小为 2 的 url 时,倒数第三行(“{currImageIndex}”)显示 1,然后是 2,然后是 3,依此类推。它永远不会重置。我的 console.log(url.length) 打印 2,我的 console.log(currImageIndex) 打印 0。我想要发生的是 currImageIndex 应该是 0, 1, 0, 1, 0, 1,.... 谁能帮忙我明白我做错了什么吗?谢谢!

原因是在 setInterval 上传递的回调仅访问第一次渲染中的 currImageIndex 变量,而不是渲染时的新值,因此您将始终获得 currImageIndex = 0 并传递条件 if (currImageIndex + 1 < url.length)。我更新了您的代码并使其按您的期望工作:https://stackblitz.com/edit/react-y6fqwt

问题

这里的问题是,当在间隔计时器中设置 increment 回调时,初始渲染中的 currImageIndex 状态值已过时。您正确地使用功能状态来增加 currImageIndex 值,但是 increment 函数体中的 currImageIndex 值始终是初始状态值 [=18] =].

解决方案

这里的一个常见解决方案是使用 React ref 来缓存 currImageIndex 状态,以便可以在回调中访问当前值。

示例:

const [currImageIndex, setCurrImageIndex] = useState(0);
const currentImageIndexRef = useRef();

useEffect(() => {
  // cache the current state value
  currentImageIndexRef.current = currentImageIndex;
}, [currImageIndex]);

const increment = () => {
  // access the current state value
  if (currentImageIndexRef.current + 1 < url.length) {
    setCurrImageIndex((oldCount) => oldCount + 1)
  }
  else {
    setCurrImageIndex(0)
  }
}

useEffect(() => {
  const id = setInterval(increment, 1000);
  return () => clearInterval(id);
}, []);

一个更简单的解决方案是只访问 url 值,该值在组件范围内通过状态更新器回调关闭。使用三元运算符检查当前值加 1 是否仍然小于 url 长度和状态 + 1,否则通过返回 0 重置。但是请注意,如果 url 数组是动态的并且长度发生变化,则此解决方案会因同样的原因而中断,初始渲染中的 url 值将在范围内关闭。

const increment = () => {
  setCurrImageIndex((count) => count + 1 < url.length ? count + 1 : 0);
}

而 IMO 最简单的解决方案是始终增加 currImageIndex 状态并取 url 长度的模数以始终计算有效的范围内索引值。如果 url 数组发生变化,这并不重要,因为模数函数将 始终 计算出有效索引。

const { url } = props;
const [index, setIndex] = useState(0);
const increment = () => setIndex(i => i + 1);

useEffect(() => {
  const id = setInterval(increment, 1000);
  return () => clearInterval(id);
}, []);

const currImageIndex = index % url.length;

return (
  <div className={styles.container}>
    <div className={styles.header}>Preview of GIF:</div>
    <img src={url[currImageIndex]} alt="GIF Preview" className={styles.image} />
    <div>{currImageIndex}</div>
  </div>
);