React(Inertia)中Laravel-Echo广播出现异常结果求助
Laravel Echo + Inertia(React) 实时广播重复接收问题排查
用PHP/Blade结合Pusher CDN实现实时功能时,提交表单后广播事件正常,其他客户端能正确接收消息。但切换到Inertia(React)+Laravel-Echo实现时,出现单次点击后接收端内容多次渲染的异常:
- 发起广播的客户端(Client1)同时收到自己的广播响应和接收端消息
- 其他客户端(Client2)重复收到两次接收端消息
Blade + Pusher CDN 代码
<!DOCTYPE html> <html lang="{{ str_replace('_', '-', app()->getLocale()) }}"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title inertia>{{ config('app.name', 'Laravel') }}</title> <!-- Fonts --> <link rel="preconnect" href="https://fonts.bunny.net"> <link href="https://fonts.bunny.net/css?family=figtree:400,500,600&display=swap" rel="stylesheet" /> <script src="https://js.pusher.com/8.2.0/pusher.min.js"></script> <!-- Scripts --> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js" integrity="sha512-v2CJ7UaYy4JwqLDIrZUI/4hqeoQieOmAZNXBeQyjo21dadnwR+8ZaIJVT8EE2iyI61OV8e6M8PP2/4hpQINQ/g==" crossorigin="anonymous" referrerpolicy="no-referrer"></script> </head> <body class="font-sans antialiased"> <div className="grid place-items-center"> <h1 className="text-slate-800 text-center p-8 text-7xl font-bold uppercase"> Test Page </h1> <p className="text-slate-500 px-8 text-center mt-3 text-2xl font-bold"> This page is dedicated to test a Real Time Chat in React, using Laravel Echo and Pusher. Request will be done using Jquery </p> <form> <button id='btn' className="mt-8 text-center border border-slate-800 bg-slate-800 text-white font-bold text-2xl p-3 rounded" > Broadcast </button> </form> </div> <div className="mt-48 grid place-items-center"> <span id="placeholder"></span> </div> </body> <script> // Enable pusher logging - don't include this in production Pusher.logToConsole = true; const pusher = new Pusher("{{ config('broadcasting.connections.pusher.key') }}", {cluster: "eu"}); const channel = pusher.subscribe('public'); channel.bind('chat', function(data){ $.post('/receive', { _token: '{{csrf_token()}}', message: "Hello, From JS", }).done(res => $("#placeholder").last().after(res)); }); $("form").submit(function(e){ e.preventDefault(); $.ajax({ url: '/broadcast', method:"POST", headers: { "X-Socket-Id": pusher.connection.socket_id }, data: { _token: "{{ csrf_token() }}", message: "Hello,From JS" } }).done(res => $("#placeholder").last().after(res)); }) </script> </html>
Inertia(React) + Laravel-Echo 代码
import React from "react"; import $ from "jquery"; import Echo from "laravel-echo"; import Pusher from "pusher-js"; import { CSRF_TOKEN } from "@/Utils/forms"; function Test() { window.Pusher = Pusher; window.Pusher.logToConsole = true; window.Echo = new Echo({ broadcaster: "pusher", key: "c804af4b0e3beefe4880", cluster: "eu", }); window.Echo.channel("public").listen(".chat", function (data) { $.post("/receive", { _token: `${CSRF_TOKEN}`, message: "Hello, From JS", }).done((res) => $("#placeholder").last().after(res)); }); const handleSubmit = (e) => { e.preventDefault(); $.ajax({ url: "/broadcast", method: "POST", headers: { "X-Socket-Id": window.Echo.socketId(), }, data: { _token: CSRF_TOKEN, message: "Hello, From JS", }, }).done((res) => $("#placeholder").last().after(res)); }; return ( <> <div className="grid place-items-center"> <h1 className="text-slate-500 text-center p-8 text-7xl font-bold uppercase"> Test Page </h1> <p className="text-slate-300 px-8 text-center mt-3 text-2xl font-bold"> This page is dedicated to test a Real Time Chat in React, using Laravel Echo and Pusher. Request will be done using Jquery </p> <form onSubmit={handleSubmit}> <button type="submit" className="mt-8 text-center border border-slate-800 bg-slate-800 text-white font-bold text-2xl p-3 rounded" > Broadcast </button> </form> </div> <div className="mt-48 grid place-items-center"> <span id="placeholder"></span> </div> </> ); } export default Test;
预期结果
Client 1: Hello,From JS broadcast Client 2: Hello, From JS receiver
实际结果
Client 1: Hello, From JS receiver; Hello,From JS broadcast Client 2: Hello, From JS receiver; Hello, From JS receiver
问题原因及解决方案
核心问题1:组件重复注册Echo监听
React组件每次渲染时都会执行函数内部代码,导致每次渲染都创建新的Echo实例并重复绑定chat事件,同一个事件被多次触发,进而重复渲染内容。
核心问题2:事件名称格式不一致
Blade代码绑定的是chat事件,React代码监听的是.chat(带前缀点号),Laravel Echo默认会给事件添加命名空间前缀,格式不匹配可能导致重复触发或事件识别异常。
解决方案步骤
用
useEffect管理Echo生命周期
仅在组件挂载时初始化一次Echo并绑定事件,卸载时清理监听,避免重复注册:import React, { useEffect } from "react"; import $ from "jquery"; import Echo from "laravel-echo"; import Pusher from "pusher-js"; import { CSRF_TOKEN } from "@/Utils/forms"; function Test() { useEffect(() => { window.Pusher = Pusher; window.Pusher.logToConsole = true; const echoInstance = new Echo({ broadcaster: "pusher", key: "c804af4b0e3beefe4880", cluster: "eu", }); // 统一事件名称为chat,和Blade代码保持一致 const listener = echoInstance.channel("public").listen("chat", function (data) { $.post("/receive", { _token: CSRF_TOKEN, message: "Hello, From JS", }).done((res) => $("#placeholder").last().after(res)); }); // 组件卸载时清理监听和Echo实例 return () => { echoInstance.channel("public").stopListening("chat"); echoInstance.disconnect(); }; }, []); // 空依赖数组确保只执行一次 const handleSubmit = (e) => { e.preventDefault(); // 从局部echoInstance获取socketId,避免全局变量问题 const socketId = window.Echo?.socketId(); $.ajax({ url: "/broadcast", method: "POST", headers: socketId ? { "X-Socket-Id": socketId } : {}, data: { _token: CSRF_TOKEN, message: "Hello, From JS", }, }).done((res) => $("#placeholder").last().after(res)); }; return ( <> {/* 页面结构保持不变 */} <div className="grid place-items-center"> <h1 className="text-slate-500 text-center p-8 text-7xl font-bold uppercase"> Test Page </h1> <p className="text-slate-300 px-8 text-center mt-3 text-2xl font-bold"> This page is dedicated to test a Real Time Chat in React, using Laravel Echo and Pusher. Request will be done using Jquery </p> <form onSubmit={handleSubmit}> <button type="submit" className="mt-8 text-center border border-slate-800 bg-slate-800 text-white font-bold text-2xl p-3 rounded" > Broadcast </button> </form> </div> <div className="mt-48 grid place-items-center"> <span id="placeholder"></span> </div> </> ); } export default Test;统一事件名称
将React中的监听事件改为chat,去掉前缀点号,和Blade代码、后端广播的事件名称保持一致。正确传递
X-Socket-Id
确保X-Socket-Id正确传递,Laravel会自动排除当前客户端接收自己的广播,解决Client1收到自身消息的问题。
额外建议
- 避免在React中用jQuery操作DOM,改用React状态管理(如
useState)渲染消息,更符合React数据流模式,减少DOM操作引发的异常。 - 检查后端广播代码,确保使用
toOthers()方法排除当前客户端,同时确认事件名称与前端完全匹配。
内容的提问来源于stack exchange,提问作者Speed
相关产品推荐
相关产品推荐

