diff --git a/packages/laravel-echo/src/channel/channel.ts b/packages/laravel-echo/src/channel/channel.ts index 45e574f6..0fd50094 100644 --- a/packages/laravel-echo/src/channel/channel.ts +++ b/packages/laravel-echo/src/channel/channel.ts @@ -10,6 +10,12 @@ export abstract class Channel { */ options: EchoOptionsWithDefaults; + /** + * The name for Broadcast Notification Created events. + */ + notificationCreatedEvent: string = + ".Illuminate\\Notifications\\Events\\BroadcastNotificationCreated"; + /** * Listen for an event on the channel instance. */ @@ -26,10 +32,7 @@ export abstract class Channel { * Listen for an event on the channel instance. */ notification(callback: CallableFunction): this { - return this.listen( - ".Illuminate\\Notifications\\Events\\BroadcastNotificationCreated", - callback, - ); + return this.listen(this.notificationCreatedEvent, callback); } /** @@ -37,6 +40,13 @@ export abstract class Channel { */ abstract stopListening(event: string, callback?: CallableFunction): this; + /** + * Stop listening for notification events on the channel instance. + */ + stopListeningForNotification(callback: CallableFunction): this { + return this.stopListening(this.notificationCreatedEvent, callback); + } + /** * Stop listening for a whisper event on the channel instance. */ diff --git a/packages/react/src/hooks/use-echo.ts b/packages/react/src/hooks/use-echo.ts index 800e13e9..2697aa9e 100644 --- a/packages/react/src/hooks/use-echo.ts +++ b/packages/react/src/hooks/use-echo.ts @@ -229,11 +229,15 @@ export const useEchoNotification = < return; } + result.channel().stopListeningForNotification(cb); + listening.current = false; }, [cb]); useEffect(() => { listen(); + + return () => stopListening(); }, dependencies.concat(events.current)); return { diff --git a/packages/react/tests/use-echo.test.ts b/packages/react/tests/use-echo.test.ts index 698c85f4..e545b548 100644 --- a/packages/react/tests/use-echo.test.ts +++ b/packages/react/tests/use-echo.test.ts @@ -11,6 +11,7 @@ vi.mock("laravel-echo", () => { listen: vi.fn(), stopListening: vi.fn(), notification: vi.fn(), + stopListeningForNotification: vi.fn(), }; const mockPublicChannel = { @@ -1040,10 +1041,11 @@ describe("useEchoNotification hook", async () => { echoModule.useEchoNotification(channelName, mockCallback), ); - expect(echoInstance.private).toHaveBeenCalledWith(channelName); + const channel = echoInstance.private(channelName); expect(() => unmount()).not.toThrow(); + expect(channel.stopListeningForNotification).toHaveBeenCalled(); expect(echoInstance.leaveChannel).toHaveBeenCalledWith( `private-${channelName}`, ); @@ -1112,8 +1114,11 @@ describe("useEchoNotification hook", async () => { expect(channel.notification).toHaveBeenCalledTimes(1); result.current.stopListening(); + expect(channel.stopListeningForNotification).toHaveBeenCalled(); + result.current.listen(); + // notification should still only be called once due to initialized check expect(channel.notification).toHaveBeenCalledTimes(1); }); diff --git a/packages/vue/src/composables/useEcho.ts b/packages/vue/src/composables/useEcho.ts index b906c7f7..a50d0354 100644 --- a/packages/vue/src/composables/useEcho.ts +++ b/packages/vue/src/composables/useEcho.ts @@ -237,6 +237,7 @@ export const useEchoNotification = < return; } + result.channel().stopListeningForNotification(cb); listening.value = false; }; @@ -244,6 +245,10 @@ export const useEchoNotification = < listen(); }); + onUnmounted(() => { + stopListening(); + }); + return { ...result, /** diff --git a/packages/vue/tests/useEcho.test.ts b/packages/vue/tests/useEcho.test.ts index 98998fc6..5c9c8efb 100644 --- a/packages/vue/tests/useEcho.test.ts +++ b/packages/vue/tests/useEcho.test.ts @@ -134,6 +134,7 @@ vi.mock("laravel-echo", () => { listen: vi.fn(), stopListening: vi.fn(), notification: vi.fn(), + stopListeningForNotification: vi.fn(), }; const mockPublicChannel = { @@ -923,10 +924,11 @@ describe("useEchoNotification hook", async () => { undefined, ); - expect(echoInstance.private).toHaveBeenCalledWith(channelName); + const channel = echoInstance.private(channelName); wrapper.unmount(); + expect(channel.stopListeningForNotification).toHaveBeenCalled(); expect(echoInstance.leaveChannel).toHaveBeenCalledWith( `private-${channelName}`, );