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

React中visibilityChange事件传递视频观看时间参数未更新问题

问题:页面跳转时visibilityChange触发的API未传递最新状态数据

需求是在用户关闭、刷新页面或跳转到其他页面时,通过visibilityChange事件将视频观看时间等数据通过API同步到服务器。目前跳转页面时API能正常调用,但传递的played_time、MET_value等参数未更新为最新值,日志显示viewTime为空字符串、MET为undefined。

原因分析

核心问题是闭包陷阱:

  • 初始渲染时,useEffect的依赖数组为空,导致内部绑定的sendViewTime函数是组件首次渲染时创建的版本
  • 这个旧函数捕获的是初始状态的played_time和MET_value,后续状态更新后,事件监听器依然绑定的是旧函数,触发时自然拿不到最新值
  • 另外,useEffect使用async是错误的,因为useEffect的返回值必须是清理函数,async会返回Promise,导致清理逻辑失效

解决方案

1. 用useCallback包裹sendViewTime,确保捕获最新状态

通过useCallback缓存函数,只有当依赖的状态/ props变化时,才重新创建函数,保证函数始终能访问到最新值:

import { useCallback } from 'react';

const sendViewTime = useCallback(async () => {
  if (document.visibilityState === "hidden") {
    console.log("文档已隐藏");
    const value = localStorage.getItem("jwt");
    const initialValue = JSON.parse(value);
    console.log("要发送的观看时长:", played_time);
    const params = {
      video_id: video_id_url,
      viewTime: played_time,
      MET: MET_value,
      weight: "",
    };

    console.log("请求参数:", params);
    try {
      const res = await setEffort(params, initialValue);
      console.log("API响应:", res);
    } catch (err) {
      console.error("API调用失败:", err);
    }
  } else {
    console.log("文档恢复可见");
  }
}, [played_time, MET_value, video_id_url]); // 列出所有用到的状态和props作为依赖

2. 修复useEffect的事件绑定逻辑

移除async,并将sendViewTime加入依赖数组,确保每次函数更新时重新绑定监听器:

useEffect(() => {
  console.log("绑定visibilityChange监听器");
  window.addEventListener("visibilitychange", sendViewTime);
  
  // 清理函数:组件卸载或依赖变化时移除监听器
  return () => {
    console.log("移除visibilityChange监听器");
    window.removeEventListener("visibilitychange", sendViewTime);
  };
}, [sendViewTime]); // 依赖sendViewTime,函数更新时重新绑定

3. 优化onProgress的日志输出(可选)

由于setPlayed_time是异步更新状态,直接打印played_time会显示旧值,改成打印刚计算的time_2更准确:

onProgress={(time) => {
  console.log("当前播放时间:", time);
  const time_1 = Math.round(time.playedSeconds);
  const time_2 = JSON.stringify(time_1);
  setPlayed_time(time_2);
  console.log("更新后的观看时长:", time_2); // 直接打印最新计算值
}}

关键说明

  • useCallback的依赖必须完整,否则依然会出现闭包陷阱
  • 禁止在useEffect中使用async,因为这会导致清理函数无法正常执行,可能引发内存泄漏
  • 每次sendViewTime更新时,useEffect会先移除旧的监听器,再绑定新的,避免重复绑定和旧函数执行

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 21:25:19