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

React.js:如何让setInterval感知状态变量的变化?

问题:setInterval无法感知React状态变量的变化

我遇到一个问题:setInterval函数中的if语句始终将tracking状态变量判定为false。该变量初始值为false,但我后续在未展示的函数中将其状态改为true,然而setInterval仍认为它是false。

有没有办法确保setInterval能感知变量或状态的变化?

我尝试过使用localStorage变量、useRef React钩子,以及useInterval钩子,但都没有效果。

相关JavaScript代码:

useEffect(()=> {
  Promise.all([
  faceapi.nets.tinyFaceDetector.loadFromUri('/models'),
  faceapi.nets.faceLandmark68Net.loadFromUri('/models'),
  faceapi.nets.faceRecognitionNet.loadFromUri("/models"),
  faceapi.nets.faceExpressionNet.loadFromUri('/models')
  ]).then(()=> {
    if(navigator.mediaDevices.getUserMedia) {
      navigator.mediaDevices
        .getUserMedia({ audio:false, video:true })
        .then((stream) => {
          console.log(stream)
          const video = document.getElementById("video");
          videoRef.current.srcObject = stream;
          console.log("video is " + video)
          video.play();
          setisLoaded(true);
          addEvent();
        })
        .catch((e) => {
          console.log(e);
        });
    }
  });
  }, []);

    const addEvent = () => {
      console.log("Inside")
      const video = document.getElementById("video");
      video.addEventListener("play", () => {
        const canvas = faceapi.createCanvas(video);
        canvas.id = "canvas";
        document.querySelector("#video").append(canvas);
        document.body.append(canvas);
        const displaySize = { width:width, height:height };
        faceapi.matchDimensions(canvas, displaySize);
        setInterval(async () => {
          const detections = await faceapi.detectAllFaces('video', new faceapi.TinyFaceDetectorOptions()).withFaceLandmarks().withFaceExpressions();
          const resizedDetections = faceapi.resizeResults(detections, displaySize);
          canvas
            .getContext("2d")
            .clearRect(0, 0, canvas.width, canvas.height);
          faceapi.draw.drawFaceExpressions(canvas, resizedDetections);
          if(resizedDetections[0].expressions && tracking) {
            setData(arr => [...arr, resizedDetections[0].expressions]);
          }
        }, 100);
      })
    }

解决方案

问题根源

这是典型的闭包陷阱:setInterval的回调函数在组件初始渲染时被创建,它捕获的是当时tracking状态的快照值。React状态更新会触发组件重新渲染,但已经创建的setInterval回调不会重新生成,所以它始终使用初始渲染时的tracking值,无法感知后续的状态变化。

方法一:用useRef同步最新状态

useRef的current属性是可变的,不会被闭包锁定,能始终拿到最新的状态值:

  1. 创建ref并同步tracking的最新值:
// 在组件顶部定义
const trackingRef = useRef(tracking);

// 监听tracking变化,实时更新ref
useEffect(() => {
  trackingRef.current = tracking;
}, [tracking]);
  1. 修改setInterval中的判断逻辑,使用trackingRef.current:
setInterval(async () => {
  // ... 其他代码保持不变
  if(resizedDetections[0].expressions && trackingRef.current) {
    setData(arr => [...arr, resizedDetections[0].expressions]);
  }
}, 100);

方法二:自定义useInterval钩子

自己实现一个能自动感知依赖变化的useInterval,确保每次回调都能拿到最新的状态:

// 在组件外部或内部定义自定义钩子
function useInterval(callback, delay) {
  const savedCallback = useRef();

  // 保存最新的回调函数,确保依赖变化时更新
  useEffect(() => {
    savedCallback.current = callback;
  }, [callback]);

  // 管理定时器的创建与销毁
  useEffect(() => {
    function tick() {
      savedCallback.current();
    }
    if (delay !== null) {
      const intervalId = setInterval(tick, delay);
      return () => clearInterval(intervalId);
    }
  }, [delay]);
}

然后替换原生setInterval,直接使用这个钩子:

// 在addEvent的play事件回调中:
useInterval(async () => {
  const detections = await faceapi.detectAllFaces('video', new faceapi.TinyFaceDetectorOptions()).withFaceLandmarks().withFaceExpressions();
  const resizedDetections = faceapi.resizeResults(detections, displaySize);
  canvas.getContext("2d").clearRect(0, 0, canvas.width, canvas.height);
  faceapi.draw.drawFaceExpressions(canvas, resizedDetections);
  if(resizedDetections[0].expressions && tracking) {
    setData(arr => [...arr, resizedDetections[0].expressions]);
  }
}, 100);

使用这个钩子时,确保tracking被包含在回调函数的依赖中(ESLint会自动提示),这样每次tracking变化时,回调函数会更新,钩子就能拿到最新的状态值。


内容的提问来源于stack exchange,提问作者ilovetocode123

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 21:24:55