Trouble Maintaining WebSocket Connection Using Shared Worker During Reload in React

38 Views Asked by At

I'm trying to implement a component in React that handles initialization and communication with a shared WebSocket across the entire application. I've created the following WithWebSocket component that utilizes a Shared Worker to manage communication with the WebSocket.

The code works correctly in some cases, but I'm facing difficulties in others. Here's the code:

import React, { createContext, useContext, useState, useEffect, useRef } from 'react';
import PropTypes from 'prop-types';
import { getAuthCookieDecoded } from 'utils/auth';
import { useLocation } from 'react-router-dom';
import { setToStorage, getFromStorage } from 'utils/storage';
import Axios from 'axios';
import Cookies from 'js-cookie';
import { DOMAIN } from 'config/endpoints';

const URL_WEBSOCKET = `wss://wes.not${DOMAIN}`;

export const WebSocketContext = createContext(null);


const { tcode: janisClient = '' } = getAuthCookieDecoded() || {};



const WithWebSocket = ({ children }) => {
    const [websocketInfo, setWebsocketInfo] = useState(null);
    const [wsToken, setWsToken] = useState('');

    const workerRef = useRef(null);
    const { pathname } = useLocation();
    const enablePaths = ['/delivery/shipping/pick-up', '/delivery/shipping/express'];
    const activeViews = enablePaths.includes(pathname);

    const getAuthToken = async () => {
        const secret = Cookies.get('JANIS_ACCESS_TOKEN');

        try {
            const { data: apiData } = await Axios.get(`https://notification${DOMAIN}/api/access-token`, {
                headers: {
                    'Janis-Api-Secret': secret,
                    'Janis-Api-Key': 'Bearer',
                    'Janis-Client': janisClient
                }
            });
            setWsToken(apiData.token);
        } catch (err) {
            console.warn({ err });
            setWsToken('');
        }
    };

    useEffect(() => {
        if (!wsToken && activeViews) {
            getAuthToken();
        }
    }, []);

    useEffect(
        () => {
            try {
                if (typeof SharedWorker === 'undefined')
                    throw new Error('This browser does not support Shared Workers');

                if (!wsToken || workerRef.current) return;

                /*  Necesita estarse instanciando para que funcione el worker   */
                /* Validar dentro del worker */
                const sharedWorkerInfo = getFromStorage('webSocketWorker');

                workerRef.current = new SharedWorker(`/static/js/webSocketWorker.js`, {
                    name: 'webSocketWorker'
                });

                setToStorage('webSocketWorker', janisClient, 10);

                if (workerRef.current) {
                    workerRef.current.port.postMessage({
                        URL: URL_WEBSOCKET.concat(`?authToken=${wsToken}`),
                        janisClient,
                        enablePaths: activeViews,
                        cacheWorkerInfo: sharedWorkerInfo
                    });

                    workerRef.current.port.onmessage = event => {
                        let dataToJson = null;

                        try {
                            dataToJson = JSON.parse(event.data);
                        } catch (reason) {
                            dataToJson = String(event.data);
                        }

                        const response = dataToJson.push || dataToJson.detail || dataToJson;

                        if (response) {
                            const parseResponse = typeof response === 'string' ? JSON.parse(response) : response;
                            parseResponse.event = dataToJson.event || '';

                            setWebsocketInfo(parseResponse);
                        }
                    };

                    workerRef.current.port.onerror = async event => {
                        console.error('error websocket :>> ', event);
                    };
                }
            } catch (error) {
                console.error(error.message);
            }
        },
        [wsToken]
    );

    return <WebSocketContext.Provider value={websocketInfo}>{children}</WebSocketContext.Provider>;
};

Code for the Shared Worker:

/* eslint-disable func-names */

const ports = [];
let ws;
let client;

const cleanPorts = port => {
    const index = ports.indexOf(port);
    if (index !== -1) {
        ports.splice(index, 1);
    }
};
const sendDataToWebSocket = (websocket, Jclient) => {
    websocket.send(
        JSON.stringify({
            action: 'subscribe',
            data: {
                events: ['delivery:shipping:pickup', 'delivery:shipping:express'],
                client: Jclient
            }
        })
    );
};
this.onconnect = event => {
    const port = event.ports[0];

    ports.push(port);

    port.onmessage = ev => {
        const message = ev.data;
        const { enablePaths, janisClient, URL, cacheWorkerInfo } = message;

        if (!enablePaths || !janisClient || (cacheWorkerInfo === janisClient && ws)) return;

        if (client !== janisClient && ws) {
            ws.close();
            cleanPorts(port);
        }

        if (!client) ws = ws || new WebSocket(URL);

        ws.onopen = () => {
            sendDataToWebSocket(ws, janisClient);
            client = janisClient;
        };

        ws.onmessage = e => {
            const websocketMessage = e.data;

            port.postMessage(websocketMessage);

            ports.forEach(p => {
                p.postMessage(websocketMessage);
            });
        };

        ws.onclose = () => {
            cleanPorts(port);
        };
    };
};

I tried saving the worker's information in local storage to validate it from the worker's side during a reload, but upon reloading, it no longer receives information in the WebSocket connection it maintains. I believe the issue lies in the fact that upon pressing F5, the component is rebuilt, generating another token and causing the entire functionality to reset.

0

There are 0 best solutions below