@r_wohl/web-channel-message
TypeScript icon, indicating that this package has built-in type declarations

3.0.2 • Public • Published

Web Channel Message

A light-weight library that lets multiple browser sessions interact with each other, through one or more Shared Workers.



Note: Package name might change soon as -unlike initially intended- it does not directly use the Web Channel Message API. Instead, the package functions as an abstraction layer around the SharedWorker API



Example


const channel = new SharedWebChannel();

channel.registerCallback("logout", logoutUser);

function handleClick() {
    channel.sendMessage({
      type: "callback",
      action: "all",
      callbackKey: "logout",
    });
}

For a working example check out this demo of a NextJS application that uses this library to synchronize state between instances of the same application in different tabs/windows.


Install


NPM

npm i @r_wohl/web-channel-message

YARN

yarn add @r_wohl/web-channel-message

Usage


To use this library, first instantiate a new SharedWebChannel, which is a wrapper around the SharedWorker API:


import { SharedWebChannel } from "@r_wohl/web-channel-message";

...

const channel = new SharedWebChannel();

If you -for some reason- want to use more then one shared worker, you can instantiate SharedWebChannel with a name argument:


const anotherChannel = new SharedWebChannel('second-channel');

Tracking open connections



As of version 3.0.0 you can keep track of the number of open connections when the SharedWebChannel is instantiated, as well as register a callback that is called when this number changes:


const numberOfConnections = channel.connections;

...

channel.onConnectionsUpdate(myCustomHandler);

If a callback is registered to be executed when the number of open connections changes, it will receive the new number of connections as input.


NOTE: Updating channel.connections when a page/session is closed relies on beforeunload/unload/pagehide events, which do not fire in some browsers/circumstances. See caveats.


Sending messages



You can now send a message to all instances of your application with the sendMessage method:


  function handleClickColor(color: BackgroundColor) {
    channel.sendMessage({
      type: "callback",
      action: "all",
      payload: color,
      callbackKey: "set-bg-color",
    });
  }

For the type property, you can choose between callback mode and observer mode, which determines how the message is handled when it is received.


For the action property, you can choose between "all" and "broadcast". Messages with "all" will be sent to all open connections, including the instance from which it was sent. Messages with "broadcast" will be sent to all other connections.


The payload property is optional. In "callback" mode this will be the input for your registered callback function. The same is true for "observer" mode, but in this case you have more control over what happens with the data in this property (see observer section)


Furthermore, in "callback" mode you'll need to specify a callbackKey. In "observer" mode you can specify a key as well, if you want the message to be received by specific ChannelObservers only:


    channel.sendMessage({
      type: "observer",
      action: "broadcast",
      key: "my-custom-event"
    });

Handling messages



When messages are received in the SharedWebChannel you can either handle them with a registered callback, or with a ChannelObserver.


Callback



The easiest way to handle message is to register a callback function, together with a specific key:


channel.registerCallback("set-bg-color", setBgColor);

Whenever a message is received with that specific key in the callbackKey property, the registered callback function is executed with the value of the received payload property as input.


Observer



Handling incoming messages with ChannelObserver instances gives you more control over data flow between different instances of your application.

Start with importing the ChannelObserver:


import { ChannelObserver } from "@r_wohl/web-channel-message";

You can now make a ChannelObserver instance:


    const observer = new ChannelObserver(channel, (data) => {
      const payload = data.payload as SomeCustomType;

      doSomethingWithPayload(payload)
    });

Optionally, you can specify a key when instantiating the ChannelObserver:

    const observer = new ChannelObserver(channel, (data) => {
      const payload = data.payload as SomeCustomType;

      handlePayload(payload)
    }, "my-custom-key");

For any ChannelObserver with a key, it will only be updated when messages are received with either that specific key, or with the message key property set to "all".


Please note that for this reason, you shouldn't set the message key to either "all" or "default" for messages in "observer" mode (the "default" key is set for ChannelObserver instances where no key is specified).


Finally, you can unsubscribe a ChannelObserver from the SharedWebChannel subject when appropriate, for instance in React.useEffect's return:


  useEffect(() => {
    const observer = new ChannelObserver(channel, (data) => {
      const payload = data.payload as SomeCustomType;
      if (payload) {
        handlePayload(payload);
      }
    });

    return () => {
      channel.subject.unsubscribe(observer);
    };

  }, []);

Caveats

Shared Worker modules compatibility


Not every browser supports Shared Worker modules at the moment. If instantiating a Shared Worker fails in the SharedWebChannel constructor, the SharedWebChannel will detect this and fall back to executing callbacks and updating ChannelObserver instances for that application instance only.


Updating connection status


It is not possible to accurately keep track of the number of active application sessions in all circumstances and on all browsers in a straightforward manner. In almost all environments closing a tab/navigating away/reloading the page will cause the connection count to be updated properly. However, on iOS -for example-, terminating a page with a close button does not cause a unique event to fire that can be identified to close that connection port inside the shared worker. So until I come up with a better solution, the connection count cannot be fully trusted in environments where closing/reloading pages can be done without firing beforeunload/unload/pagehide events.


Contributors

This is my first NPM library, and I'd would be delighted if anyone would like to comment on the code or contribute in any way. Whether it be a long list of my failures or compliments on the idea/implementation, your input is more than welcome!

Package Sidebar

Install

npm i @r_wohl/web-channel-message

Weekly Downloads

5

Version

3.0.2

License

ISC

Unpacked Size

75.2 kB

Total Files

19

Last publish

Collaborators

  • r_wohl