Last active
June 13, 2023 21:16
-
-
Save TheLexoPlexx/6a98a1930cb2647a7e6215ce9c3494c4 to your computer and use it in GitHub Desktop.
socket.io in Nextjs 13.4.3
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
/* | |
Contains 3 Files: | |
- components/socket/socketProvider.tsx (Client-Connection) | |
- components/socket/webSocketStatus.tsx (Client-Status and useSocket-Example) | |
- websocket/src/index.ts (Server) | |
Websocket-Server is done externally because nextjs-API-Routes are simply not made for that except you hate yourself. | |
"next": "^13.4.3", | |
"socket.io-client": "^4.6.2", | |
"express": "^4.18.2", | |
"socket.io": "^4.6.2" | |
*/ | |
//components/socket/socketProvider.tsx | |
"use client" | |
import { ReactNode, createContext, useContext, useEffect, useMemo, useState } from 'react'; | |
import { Socket, io } from 'socket.io-client'; | |
export enum SocketStatus { | |
CONNECTED = "connected", | |
DISCONNECTED = "disconnected", | |
TIMEOUT = "timeout", | |
LOADING = "loading" | |
} | |
interface SocketContextValue { | |
socket: Socket; | |
status: SocketStatus; | |
} | |
const newSocket = io("http://localhost:3300/", { | |
transports: ["websocket"], | |
autoConnect: false | |
}); | |
export const SocketContext = createContext<SocketContextValue>({ | |
socket: newSocket, | |
status: SocketStatus.LOADING, | |
}); | |
export const SocketProvider: React.FC<{ children: ReactNode }> = ({ children }) => { | |
const [socket, setSocket] = useState<Socket>(newSocket); | |
const [status, setStatus] = useState<SocketStatus>(SocketStatus.LOADING); | |
const events = useMemo( | |
() => [ | |
{ | |
event: "connect", | |
callback: () => { | |
console.log("connected", newSocket.id); | |
setStatus(SocketStatus.CONNECTED); | |
} | |
}, | |
{ | |
event: "disconnect", | |
callback: () => { | |
console.log("disconnected", newSocket.id); | |
setStatus(SocketStatus.DISCONNECTED); | |
} | |
}, | |
{ | |
event: "reconnect", | |
callback: () => { | |
console.log("reconnect", newSocket.id); | |
setStatus(SocketStatus.CONNECTED); | |
} | |
}, | |
{ | |
event: "connect_error", | |
callback: (error: any) => { | |
console.log("connect_error, retrying in 5...", error.name); | |
setStatus(SocketStatus.DISCONNECTED); | |
} | |
}, | |
{ | |
event: "connect_timeout", | |
callback: () => { | |
console.log("connect_timeout"); | |
setStatus(SocketStatus.TIMEOUT); | |
} | |
}, | |
], | |
[] | |
) | |
useEffect(() => { | |
events.forEach((event) => { | |
// console.log("registering", event.event) | |
newSocket.on(event.event, (arg) => { event.callback(arg) }) | |
}) | |
newSocket.connect() | |
setSocket(newSocket); | |
return () => { | |
events.forEach((event) => { | |
// console.log("off", event.event) | |
newSocket.off(event.event, event.callback) | |
}) | |
newSocket.disconnect(); | |
}; | |
}, [socket, events]); | |
return ( | |
<SocketContext.Provider value={{ socket, status }}> | |
{children} | |
</SocketContext.Provider> | |
); | |
}; | |
export function useSocket() { | |
const { socket, status } = useContext(SocketContext); | |
if (!socket) { | |
throw new Error('useSocket must be used within a SocketProvider'); | |
} | |
return { socket, status } | |
} | |
// ################################################ | |
// components/socket/webSocketStatus.tsx | |
"use client" | |
import { useEffect, useState } from "react"; | |
import { usePathname } from "next/navigation"; | |
import { SocketStatus, useSocket } from "./socketProvider"; | |
import { toast } from "react-toastify"; | |
export default function WebSocketStatus() { | |
let path = usePathname() | |
let { socket, status } = useSocket() | |
let [lastStatus, setLastStatus] = useState<SocketStatus>(SocketStatus.LOADING) | |
useEffect(() => { | |
if (status == SocketStatus.DISCONNECTED || status == SocketStatus.TIMEOUT) { | |
if (lastStatus !== SocketStatus.DISCONNECTED || status != SocketStatus.TIMEOUT) { | |
toast.error("Websocket-Verbindung verloren.") | |
} | |
} | |
if (status === SocketStatus.CONNECTED) { | |
if (lastStatus === SocketStatus.DISCONNECTED) { | |
toast.success("Websocket verbunden") | |
} | |
socket.emit("path", path) | |
console.log("status", status) | |
} | |
setLastStatus(status) | |
}, [status, path, lastStatus, socket]) | |
return ( | |
<p>{status}</p> | |
); | |
} | |
// ################################################ | |
// websocket/src/index.tsx | |
import express from "express" | |
import http from "http" | |
import { Server, Socket } from "socket.io" | |
import { PrismaClient } from "@prisma/client" | |
function main() { | |
const app = express() | |
const server = http.createServer(app) | |
const io = new Server(server) | |
const prisma = new PrismaClient() | |
io.on("connection", (socket: Socket) => { | |
console.log("connect", socket.id) | |
socket.onAny(async (event, ...args) => { | |
if (event == "path") { | |
console.log("[" + socket.id + "] path: ", args) | |
} else if (event == "refetch") { | |
console.log("refetch", args) | |
io.emit("refetch", args) | |
} else if (event == "prisma") { | |
console.log("prisma", args) | |
} else { | |
console.log("[" + socket.id + "] unhandled: ", event, args) | |
} | |
}) | |
socket.on("disconnect", () => { | |
console.log("disconnect", socket.id) | |
}) | |
}); | |
server.listen(3300, () => { | |
console.log("Server gestartet auf: *:3300"); | |
}); | |
} | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment