You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React中基于数组元素动态调整setInterval执行间隔的问题

问题:React中无法通过State动态调整定时任务的执行间隔

我希望通过currentLocationIndex遍历speed数组[1, 5, 6, 2, 3, 4],以此动态修改console.log(prevIndex)的输出时机,原本期望用1000 * speed[currentLocationIndex]实现间隔动态变化,但当前代码未达到预期效果,原代码如下:

import { useState } from 'react';
import './App.css';

function App() {
  const [speed, setSpeed] = useState([1, 5, 6, 2, 3, 4]);
  const [location, setLocation] = useState([10, 20, 13, 20, 10, 2]);
  const [currentLocationIndex, setCurrentLocationIndex] = useState(0);

  function updateLocation() {
    const interval = setInterval(() => {
      setCurrentLocationIndex((prevIndex) => {
        console.log(prevIndex);
        if (prevIndex < location.length - 1) {
          console.log(prevIndex);
          return prevIndex + 1;
        } else {
          clearInterval(interval);
          return prevIndex;
        }
      });
    }, 1000 * speed[currentLocationIndex]);
  }

  return (
    <div>
      <button onClick={updateLocation}>Initiate</button>
    </div>
  );
}

export default App;

问题原因

setInterval的间隔参数在调用updateLocation时就已固定,只会读取当时currentLocationIndex对应的speed值。后续currentLocationIndex更新后,setInterval不会自动重新计算间隔,导致所有循环的执行间隔都是初始值。

解决方案

改用**递归式setTimeout**替代setInterval,每次执行完当前逻辑后,根据最新的currentLocationIndex计算下一次的超时时间,同时使用useRef保存超时ID,避免重复触发和内存泄漏:

import { useState, useRef, useEffect } from 'react';
import './App.css';

function App() {
  const [speed, setSpeed] = useState([1, 5, 6, 2, 3, 4]);
  const [location, setLocation] = useState([10, 20, 13, 20, 10, 2]);
  const [currentLocationIndex, setCurrentLocationIndex] = useState(0);
  const timeoutIdRef = useRef(null); // 保存超时ID,用于清理

  function updateLocation() {
    // 先清理之前的定时任务,防止重复点击触发多个任务
    if (timeoutIdRef.current) {
      clearTimeout(timeoutIdRef.current);
    }

    // 定义递归执行的函数
    const executeNextStep = () => {
      setCurrentLocationIndex((prevIndex) => {
        console.log(prevIndex);
        if (prevIndex < location.length - 1) {
          const nextIndex = prevIndex + 1;
          // 根据下一个索引设置下一次的超时时间
          timeoutIdRef.current = setTimeout(executeNextStep, 1000 * speed[nextIndex]);
          return nextIndex;
        } else {
          timeoutIdRef.current = null; // 任务结束,清空ID
          return prevIndex;
        }
      });
    };

    // 执行第一步,初始间隔用speed[0]
    timeoutIdRef.current = setTimeout(executeNextStep, 1000 * speed[currentLocationIndex]);
  }

  // 组件卸载时清理定时任务
  useEffect(() => {
    return () => {
      if (timeoutIdRef.current) {
        clearTimeout(timeoutIdRef.current);
      }
    };
  }, []);

  return (
    <div>
      <button onClick={updateLocation}>Initiate</button>
    </div>
  );
}

export default App;

关键说明

  1. 递归setTimeout:每次更新currentLocationIndex后,根据新索引获取对应的speed值,设置下一次的超时时间,实现动态间隔。
  2. useRef保存超时ID:用于在重复点击按钮或组件卸载时清理未完成的定时任务,避免内存泄漏和重复执行。
  3. 初始执行逻辑:第一次执行的间隔使用当前currentLocationIndex对应的speed值,之后每次都基于下一个索引计算间隔。

内容的提问来源于stack exchange,提问作者Lucas Santana Figueiredo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 05:52:54