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

socket事件触发音频播放不稳定,伴AudioContext未授权启动报错

问题:Socket事件触发时音频播放不稳定,报错AudioContext未授权启动

问题描述

Socket事件触发时尝试播放音频,存在播放不稳定的情况,有时正常有时失败,控制台报错:

"The AudioContext was not allowed to start. It must be resumed (or created) after a user gesture on the page"

原代码:

import React, { useEffect, useState } from "react";
import { useSound } from "use-sound";
import notificationSound from "my_sound.mp3";
import { io } from "socket.io-client";


const Home = ({ children }) => {
  const [play] = useSound(notificationSound, { volume: 0.5 });
  const [playAudio, setPlayAudio] = useState(false);

  useEffect(() => {
    if (playAudio === true) {
      play();
      setPlayAudio(false);
    }
  }, [playAudio]);

useEffect(() => {
      const socket = io("http://localhost:8081", {
        auth: {
          token: localStorage.getItem("user_token"),
        },
      });
      socket.on('play-sound', (data) => {
        setPlayAudio(true);
        toast.success(`New Order ${data.order_id} Received!!`);
      });
}, []);
return (<div> Hello World </div>)
};

module.export = Home;

原因分析

浏览器的自动播放安全策略规定:AudioContext必须在用户主动交互(如点击、触摸页面)之后才能创建或恢复,否则会被浏览器阻塞。你的代码中,use-sound初始化的AudioContext可能还未经过用户交互授权,当Socket事件触发调用play()时,就会触发这个错误。

解决方案

通过用户主动交互激活AudioContext,同时在播放前检查上下文状态,确保可以正常播放。以下是修改后的代码:

import React, { useEffect, useState, useRef } from "react";
import { useSound } from "use-sound";
import notificationSound from "my_sound.mp3";
import { io } from "socket.io-client";

const Home = ({ children }) => {
  // 解构出sound对象,用于访问底层AudioContext
  const [play, { sound }] = useSound(notificationSound, { volume: 0.5, interrupt: true });
  const [audioActivated, setAudioActivated] = useState(false);
  // 用useRef保存socket实例,避免重复创建
  const socketRef = useRef(null);

  // 处理用户交互,激活音频上下文
  const activateAudio = () => {
    if (sound && sound.context.state === "suspended") {
      sound.context.resume();
    }
    setAudioActivated(true);
  };

  useEffect(() => {
    socketRef.current = io("http://localhost:8081", {
      auth: {
        token: localStorage.getItem("user_token"),
      },
    });

    socketRef.current.on('play-sound', (data) => {
      // 播放前检查AudioContext状态,若暂停则先恢复
      if (sound && sound.context.state === "suspended") {
        sound.context.resume().then(() => play());
      } else {
        play();
      }
      toast.success(`New Order ${data.order_id} Received!!`);
    });

    // 组件卸载时断开socket连接
    return () => socketRef.current?.disconnect();
  }, [play, sound]);

  return (
    <div>
      {/* 首次加载显示激活按钮,用户点击后授权音频 */}
      {!audioActivated && (
        <button onClick={activateAudio}>
          点击激活通知音效
        </button>
      )}
      Hello World
      {children}
    </div>
  );
};

export default Home;

关键修改说明

  1. 获取AudioContext实例:从use-sound的返回值中解构sound对象,直接访问底层的AudioContext。
  2. 用户交互激活:添加激活按钮,用户首次点击时恢复处于suspended状态的AudioContext,标记为已激活。
  3. Socket回调中检查状态:在触发播放前先判断AudioContext状态,如果还处于暂停状态,先调用resume()再执行播放。
  4. 优化Socket实例管理:用useRef保存Socket实例,避免组件重复渲染时重新创建连接,同时在组件卸载时断开连接。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 01:52:50