javascriptreactjsweb-workershared-worker

Receive messages in parent and child component in SharedWorker


I have created a sharedWorker using this script

// sharedWorker.js
importScripts('https://cdn.socket.io/4.3.1/socket.io.min.js');
let socket
let browserInstances = []
let counter = 0

onconnect = function(e) {
  const port = e.ports[0]
  browserInstances.push(port)


  if (!socket) {
    socket = io('socket-url');

    socket.on('message', (message) => {
      const msg = JSON.parse(message)
      browserInstances.map(instance => {
        instance.postMessage({type: msg.type, data: msg.data});
      });
    });
  }

}

i am receiving two types of messages from socket one is {type: 'notification', data: '12345'} and the other is {type: 'event', data: '12345'}

now there are two listeners one is my main App.tsx component and the other is in my child component.

this is my App.tsx listener

worker.port.onmessage = (event: any) => {
            console.log(event.data)
            if (event.data.type === 'notification') {
                console.log('Notification received:', event.data.data);
                // Handle notification logic here
              }
              // Do not consume the message, let it propagate to other components
              return true;
          }

and this is my childComponent.tsx listener

worker.port.onmessage = (event: any) => {
        console.log(event)
        if (event.data.type === 'event') {
          console.log('Event received:', event.data.data);
          // Handle event logic here
        }      
      }

And here is my WorkerContext.tsx code where i start the worker

// WorkerContext.ts

import React, { createContext, useEffect, useState, ReactNode } from 'react';


export const WorkerContext = createContext<any | undefined>(undefined);

interface WorkerProviderProps {
  children: ReactNode;
}


export const WorkerProvider: React.FC<WorkerProviderProps> = ({ children }) => {
  const [worker, setWorker] = useState<SharedWorker | null>(null);

  useEffect(() => {
    const sharedWorker = new SharedWorker('/sharedWorker.js');
    setWorker(sharedWorker);

    return () => {
      sharedWorker.port.close();
    };
  }, []);

  return (
    <WorkerContext.Provider value={{ worker }}>
      {children}
    </WorkerContext.Provider>
  );
};

Issue is that it is only receiving messages in App.tsx component, messages are not getting passed to childComponent.tsx but when i remove the App.tsx listener it gets passed to childComponent. How can i pass the message to App and childComponent based on data type?


Solution

  • worker.port.onmessage = ___ assigns to the onmessage property, overwriting whatever value it had before (thus removing any previous event listener set up that same way). Instead, use worker.port.addEventListener("message", ___);, so you can have multiple listeners for the event. See the EventTarget docs for addEventListener (MessagePort is a subclass of EventTarget). You also need to call start() on the port (the onmessage setter does that for you).