Last active
September 1, 2024 08:45
-
-
Save sudkumar/c84be58dd644730fd3ce0ebae98a56db to your computer and use it in GitHub Desktop.
Web-Sockets in ReactJS with PusherJs and Laravel Echo with public and private channels
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, { useEffect, useState, useMemo } from "react" | |
import Echo from "laravel-echo" | |
import Pusher from "pusher-js" | |
/** | |
* Pusher configuration | |
*/ | |
const pusherConfig = { | |
key: '<your_pusher_key_here>', | |
cluster: '<pusher_cluster>', | |
// auth endpoint for private channels | |
// e.g. for Laravel https://example.com/api/broadcasting/auth | |
authEndpoint: '<auth_endpoint_for_private_channels>' | |
} | |
/** | |
* Context for Channels | |
*/ | |
type TChannels = Echo | undefined | |
const ChannelsContext = React.createContext<TChannels>(undefined) | |
/** | |
* Channel Context Provider | |
*/ | |
export function ChannelsProvider({ | |
children, | |
authUser, | |
authToken | |
}: { | |
children: React.ReactNode, | |
authUser?: any | |
authToken?: string | |
}) { | |
const [channels, setChannels] = useState<TChannels>(undefined) | |
useEffect(() => { | |
const channels = getChannels(pusherConfig, authToken); | |
setChannels(channels) | |
return () => { | |
// disconnect from server and reset the channels | |
channels.disconnect() | |
setChannels(undefined) | |
} | |
}, [authUser, authToken]) | |
return ( | |
<ChannelContext.Provider value={channels}> | |
{children} | |
</ChannelContext.Provider> | |
) | |
} | |
/** | |
* Hook to use the channels provided via context | |
*/ | |
export function useChannels() { | |
const channels = React.useContext(ChannelsContext) | |
return channels | |
} | |
/** | |
* Use private channels | |
* It simple return the useChannels with authenticated user bindings | |
*/ | |
export function usePrivateChannels (authUserId: any) { | |
const channels = useChannels() | |
return useMemo(() => { | |
return channels && channels.private("users." + authUserId) | |
}, [channels, authUserId]) | |
} | |
/** | |
* Get the channels | |
*/ | |
function getChannels(pusherConfig: typeof pusherConfig, authToken?: string) { | |
const client = new Pusher(pusherConfig.key, { | |
cluster: pusherConfig.cluster, | |
forceTLS: true, | |
authEndpoint: pusherConfig.authEndpoint, | |
auth: authToken ? { | |
headers: { | |
// pass the authorization token when using private channels | |
Authorization: `Bearer ${authToken}`, | |
}, | |
}: undefined, | |
}) | |
const channels = new Echo({ | |
broadcaster: "pusher", | |
client: client, | |
}) | |
return channels | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, { useEffect } from 'react' | |
import { ChannelsProvider, usePrivateChannels } from "./channels" | |
// FIRST: we will wrap our application with the `ChannelsProvider` | |
function App () { | |
// get the user and the access token SOMEHOW! | |
const user = getUser() // via context or any other way | |
const token = getToken() // via local storage or any other ways | |
return <ChannelsProvider authUser={user} authToken={token}> | |
<Notifications authUserId={user.id} /> | |
</ChannelsProvider> | |
} | |
// Next, we will listen to a private channel for an event | |
// and update the data on events | |
// A channel listens to a particular event | |
// Here is an example event from Laravel when sending notifications | |
const NOTIFICATION_EVENT = | |
".Illuminate\\Notifications\\Events\\BroadcastNotificationCreated" | |
interface INotification { | |
id: number, | |
content: string | |
} | |
/** | |
* Our notification channel which notif | |
*/ | |
function useNotificationChannel ( | |
authUserId: integer, | |
onChange: (notification: INotification) => void | |
) { | |
const channels = usePrivateChannels(authUserId) | |
useEffect(() => { | |
if (channels) { | |
channels.listen(NOTIFICATION_EVENT, onChange) | |
// same as channels.notification(onChange) | |
return () => { | |
channels.stopListening(NOTIFICATION_EVENT) | |
} | |
} | |
}, [channels, onChange]) | |
} | |
export function Notifications({ authUserId }: { authUserId: integer }) { | |
const [notifications, setNotifications] = useState<Array<INotification>>([]) | |
const handleNotificationsEvent = useCallback((notification: INotification) => { | |
setNotifications( | |
existingNotifications => ([notification].concat(existingNotifications)) | |
) | |
}) | |
useNotificationChannel(authUserId, handleNotificationsEvent) | |
return <ol> | |
{notifications.map(n => <li key={n.id}>{n.content}</li>} | |
</ol> | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Please, do you it js version. and where would the configuration be in If it is used in nextjs