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

NextJS状态数组DOM可渲染但函数内访问为空问题求助

问题原因与解决方案

核心问题:闭包导致的状态捕获陷阱

你的events状态在渲染时能正常显示,但addEvent函数里访问events[0]始终为空,本质是React闭包特性引发的陷阱:

  • 组件首次渲染时,useEffect的依赖数组为空,内部的EventSource回调会捕获此时的addEvent函数。
  • 这个初始的addEvent函数引用的是组件初始化时的空events数组,后续即使events状态更新,EventSource回调里的addEvent依然是旧版本,永远访问空数组。

最优解决方案:使用函数式更新获取最新状态

直接修改addEvent函数,通过setEvents的回调参数获取最新的events状态,完全避免依赖外部的events变量:

const addEvent = (action_event: any) => {
  const transformedEvent = {
    ...JSON.parse(action_event),
    camera: "Face",
  };

  if (parseInt(transformedEvent.cont)) {
    // 通过setEvents回调拿到最新的events状态
    setEvents(prevEvents => {
      // 防御性判断,避免数组为空时报错
      if (prevEvents.length === 0) return prevEvents;
      
      const newEvent = { ...prevEvents[0] };
      newEvent.frameEnd = transformedEvent.frameEnd;
      return [newEvent].concat(prevEvents.slice(1));
    });
  } else {
    delete transformedEvent.cont;
    setEvents(prevEvents => [transformedEvent].concat(prevEvents));
  }
};

备选方案:用useCallback更新函数依赖(不推荐)

如果一定要保留直接访问events的写法,可以用useCallback包裹addEvent并加入useEffect依赖,但会导致EventSource频繁重建,可能引发连接问题:

// 导入useCallback
import React, { useState, useEffect, useCallback } from "react";

// ...

// 用useCallback包裹addEvent,依赖events更新
const addEvent = useCallback((action_event: any) => {
  const transformedEvent = {
    ...JSON.parse(action_event),
    camera: "Face",
  };

  if (parseInt(transformedEvent.cont)) {
    let newEvent = { ...events[0] };
    newEvent.frameEnd = transformedEvent.frameEnd;
    setEvents((prevEvents) => [newEvent].concat(prevEvents.slice(1)));
  } else {
    delete transformedEvent.cont;
    setEvents((prevEvents) => [transformedEvent].concat(prevEvents));
  }
}, [events]);

// 更新useEffect依赖数组,加入addEvent
useEffect(() => {
  const eventSource = new EventSource(`http://127.0.0.1:5000/video_feed`);
  eventSource.onmessage = (event) => {
    const data = JSON.parse(event.data);
    if (data.action_event != 0) {
      addEvent(data.action_event.replaceAll("'", '"'));
      setFrame(data.image);
    }
    addLivePredictions(data.actions_predictions, data.first_frame_num);
  };
  return () => {
    eventSource.close();
  };
}, [addEvent]);

关键说明

  • 渲染时能正常显示events,是因为组件每次状态更新都会重新渲染,此时拿到的是最新的events状态。
  • 而EventSource的回调因闭包特性,一直持有组件首次渲染时的addEvent引用,所以内部的events永远是空数组。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 17:19:57