Next.js Discord

Discord Forum

Help determining why a socket in context is null

Unanswered
Large oak-apple gall posted this in #help-forum
Open in Discord
Original message was deleted.

10 Replies

Large oak-apple gall
So I have a SocketContext provider like so:
import React, { createContext, useContext, useEffect, useState } from 'react';
import UserProvider, {SessionContext} from "./UserProvider";

type WebSocketContextType = {
    socket: WebSocket | null,
    subscribeToAllMessages: (callback: (message: WebsocketMessage) => void) => void;
    subscribeToMessageName: (name: string, callback: (message: WebsocketMessage) => void) => void;
    unsubscribeToMessageName: (name: string, callback: (message: WebsocketMessage) => void) => void;
    unsubscribeFromAllMessages: (callback: (message: WebsocketMessage) => void) => void;
    emit: (message: WebsocketMessage) => void;

    connected: boolean;
};



export type WebsocketMessage = {
    name: string,
    type: string,
    message: any
}

export const WebsocketContext = createContext<WebSocketContextType>({
    socket: null,
    subscribeToAllMessages: () =>{},
    subscribeToMessageName: () =>{},
    unsubscribeToMessageName: () =>{},
    unsubscribeFromAllMessages: () =>{},
    emit: () =>{},
    connected: false,
});
export const WebSocketProvider: React.FC = ({ children }) => {
    const { sessionToken } = useContext(SessionContext);
    const [socket, setSocket] = useState<WebSocket | null>(null);
    const [connected, setConnected] = useState<boolean>(false);
    const allMessageSubscribers = new Set<(message: WebsocketMessage) => void>();
    const singleMessageSubscribers = new Map<string, Set<(message: WebsocketMessage) => void>>();

    useEffect(() => {
        console.log('trying to connect')
        const newSocket = new WebSocket('ws://localhost:8044');

        newSocket.onopen = () => {
            console.log('Connected to WebSocket');
            setSocket(newSocket);
            setConnected(true);
        };

        newSocket.onerror = (err) => {
            console.log('failed to connect', err);
            setConnected(false);
        }
        newSocket.onclose = (err) => {
            console.log('socket closed', err);
            setConnected(false);
        }
        newSocket.onmessage = (event) => {
            const message = event.data.toString();
            const parsedMessage: WebsocketMessage = JSON.parse(message);
            allMessageSubscribers.forEach((subscriber) => subscriber(message));
            if (singleMessageSubscribers.has(parsedMessage.name)) {
                const subscribers = singleMessageSubscribers.get(parsedMessage.name);
                if (subscribers) subscribers.forEach((subscriber) => subscriber(message));
            }
        };

        return () => {
            if (socket) {
                socket.close();
            }
        };
    }, [sessionToken]);
 useEffect(() => {
        if (socket && connected) {
            emit({
                name: 'handshake',
                type: 'auth',
                message: {
                    'token': sessionToken
                }
            });
        }

    }, [socket, connected]);

    const subscribeToAllMessages = (callback: (message: WebsocketMessage) => void) => {
        allMessageSubscribers.add(callback);
    };

    const subscribeToMessageName = (name: string, callback: (message: WebsocketMessage) => void) => {
        if (!singleMessageSubscribers.has(name)) {
            singleMessageSubscribers.set(name, new Set<(message: WebsocketMessage) => void>([callback]));
        } else {
            const subscribers = singleMessageSubscribers.get(name);
            if (subscribers) subscribers.add(callback);
        }
    };
    const unsubscribeToMessageName = (name: string, callback: (message: WebsocketMessage) => void) => {
        if (singleMessageSubscribers.has(name)) {
            const subscribers = singleMessageSubscribers.get(name);
            if (subscribers) {
                subscribers.delete(callback);
                if (subscribers.size === 0) singleMessageSubscribers.delete(name);
            }
        }
    };

    const emit = (message: WebsocketMessage) => {
        alert(socket + ' ' + JSON.stringify(message));
        socket?.send(JSON.stringify(message));
    }
    const unsubscribeFromAllMessages = (callback: (message: WebsocketMessage) => void) => {
        allMessageSubscribers.delete(callback);
    };

    const value: WebSocketContextType = {
        socket: socket,
        subscribeToAllMessages,
        subscribeToMessageName,
        unsubscribeToMessageName,
        unsubscribeFromAllMessages,
        emit,
        connected,
    };

    return <WebsocketContext.Provider value={value}>{children}</WebsocketContext.Provider>;
};
Component:

const WheelPage: React.FC<WheelPageProps> = ({  }) => {
    //const { sessionToken, loggedIn, validatingAuth, login, logout } = useContext(SessionContext);
    const { subscribeToMessageName, unsubscribeToMessageName, emit, socket, connected } = useContext(WebsocketContext);
    const [wheel, setWheel] = useState<Wheel>();

    const onSocket = (message: WebsocketMessage) => {
        console.log(message);
        if (message.type === 'spin') wheel?.startSpinning();
    };
    const onHandshake = (message: WebsocketMessage) => {
        console.log('got handshakes ' + message);
        console.log(socket);
        if (socket) {
            const pingMessage: WebsocketMessage = {
                name: 'wheel',
                type: 'ping',
                message: null
            }
            emit(pingMessage);
        }

    };


    useEffect(() => {
        subscribeToMessageName('wheel', onSocket);
        subscribeToMessageName('handshake', onHandshake);
    }, [socket]);
....
I have a useEffect in the WebsocketContext that when connected, emits a handshake request, (server then responds with a handshake response)
So in my component, i subscribe to the handshake messages,
My component receives the handshake just fine,
But when it emits the pingMessage, socket is NULL. But socket can't be null, as i just received a socket message. The socket is also not closed.
Does anyone have any idea what the issue may be? My theory is some async stuff with useEffect but I'm not having any luck
just baffles me how the onHandshake callback can get called, but detect socket as null
Large oak-apple gall
anyone know? am i maybe using the context wrong?
i've also tried adding "connected" as the dependency in the useEffect, with and without socket too
same results
are the socket events somehow getting called in between react internal stuff maybe? seems weird that socket being null would be a side effect though