Broadcasting in React (Inertia) gives Unexpected Results

90 views Asked by At

I'm trying to code a simple Real Time application for my web app. I tried coding it in simple php/blade (using Pusher Cdn), and the programs give the expected results: when the form is submitted, a new event is broadcasted and the other client receives the message. When i try it in Inertia (using Laravel-Echo) all of a sudden, it starts doing some strange things, like rendering (multiple times) the receiver partials, with one single click.

Pusher Cdn and PHP/Blade:

<!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) and 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;

Excuse my messy code. As i said above, the problem only occurs when using Laravel-Echo

Expected results: 
    Client 1: Hello,From JS broadcast 
    Client 2: Hello, From JS receiver
Actual Results:
    Client 1:  Hello, From JS receiver; Hello,From JS broadcast
    Client 2:  Hello, From JS receiver; Hello, From JS receiver
1

There are 1 answers

0
Speed On

All right, i am publishing what solved it for me in case another developer is having the same issue.

    useEffect(() => {
        window.Pusher = Pusher;
        window.Pusher.logToConsole = true;

        window.Echo = new Echo({
            broadcaster: "pusher",
            key: "c804af4b0e3beefe4880",
            cluster: "eu",
        });

        const channel = window.Echo.channel("public");

        channel.listen(".chat", function (data) {
            $.post("/receive", {
                _token: `${CSRF_TOKEN}`,
                message: "Hello, From JS",
            }).done((res) => $("#placeholder").last().after(res));
        });

        return () => {
            // Cleanup when the component unmounts
            channel.unbind(".chat");
        };
    }, []);

    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));
    };

What i've essentialy done, is putting the code responsible for setting up pusher and listening for an event in a useEffect function, with an empty array. I used the useEffect hook to make sure we set up the event listener just once when the component loads. And to keep things tidy, I added a cleanup function in the return statement