npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

spokes

v0.5.6

Published

Pub/sub for coordinating webpage events with frameworks and 3rd parties

Downloads

19

Readme

Spokes.js

Spokes.js facilitates coordinating lifecycle events between webpage lifecycle and the lifecycles of single-page applications (SPAs) or third party analytics platforms (e.g. Google Tag Manager's data layer).

  • Pub/sub events allow for the basis of coordinating events and sharing data between components. Events are published to topics that can be subscribed to.
  • State change events are published to indicate when global state data is updated.
  • Lifecycle events can be defined for each set of events that you want to track and coordinate. By default, webpage (Page) lifecycle events are automatically processed on page load and pub/sub events are published to notify about Loaded and QueryStringParsed to hook into.
  • Promises allow resolving lifecycle events, even if they've already ocurred, in order to retrieve the related data as needed. Note, however, that lifecycle events arre also published and can be subscribed to as-they-happen with pub/sub.

Install

Run npm install spokes to add this package to your project (package.json).

Development

  • Run yarn install to get all dependencies installed.

Publish to NPM

  • Commit all changes
  • Run yarn publish to specify a new version and automatically create a commit, tag and publish (npm publish will increment the patch without prompting for a version)

Spokes + React

Share global state and pubsub events between your webpage and your React app. Use the HOC (higher order component) and hooks from spokes-react to share a Spokes instance.

TODO

  • Polyfills for ES6 and other browser things that may not be supported by all (e.g. Promise)
  • Add GTM Data Layer integration
  • Add common analytics event integration (e.g. track and identify)
  • Evaluate if we use URLSearchParams (and related polyfill) rather than the ValueStack internals
  • Evaluate if we use Map (and related polyfill) rather than the List internals

Getting Started

Setup is pretty straight forward. You need an instance of Spokes that is shared by all components that need to communicate with each other.

import Spokes from 'Spokes';
import Page from 'Spokes/lifecycle/Page';
import window from 'Spokes/dom/window';
import document from 'Spokes/dom/document';

// Create a global instance to use by your SPA or analytics data layer
const spokes = new Spokes();

// Register the Page lifecycle, which hooks into pub/sub and the ability for
// components to resolve the result of specific page events (`Loaded` and `QueryStringParsed`)
spokes.registerLifecycle('Page', lifecycle => new Page(document).load(lifecycle));

// You could make this a global variable
window._spokes = spokes;

Publish/Subscribe Events

You can easily coordinate events and data between components with publish/subscribe. Events are published to topics, and you can subscribe to topics. Within the payload value are topic, key and value.

// Subscribe to the `Page` lifecycle topic and filter on the load event that
// Spokes emits automatically. Note that the last argument is a
// `Subscription` instance, which allows you to call
// `subscription.unsubscribe()` to unsubscribe.
spokes.subscribe('Page', ({ key, value }, subscription) => {
  if (key == 'Loaded') console.log('Page:Loaded fired', value);
});

// You can also subscribe and receive the last event published to the
// topic in case you missed it, using  the `withLastEvent` option.
spokes.subscribe('Page', ({ key, value }) => {
  if (key == 'Loaded') console.log('Page:Loaded fired', value);
}, { withLastEvent: true });

// Alternatively, subscribe to all topics. In this case,
// the original event is nested inside the payload's `value` property.
spokes.subscribeAll((payload) => {
  const { topic, key, value } = payload.value;
  console.log(topic, key, value);
});

You can publish events to topics to notify other components of what's occurred.

// You can publish to a topic with  an  event name and a data value.
spokes.publish('Some Topic', 'UserRegistered', { name: 'John Doe', email: '[email protected]', phone: '123-456-7890' });

Global State & State Changes

It's simple to manage global state by utilizing a key/value store. Any key that is added or value that is updated emits an event to the StateChanged topic.

// Subscribe to changes to global state and filter on `UserProfile`.
spokes.subscribe('StateChanges', ({ key, value }) => {
  if (key == 'UserProfile') console.log('User info changed', value);
});

// Update the global state, which will publish an event to the
// `StateChanged` topic.
spokes.setState('UserProfile', { name: 'John Doe', email: '[email protected]' });

// Get the latest value.
const userProfile = spokes.getState('UserProfile');

Lifecycle Events

Lifecycle events differ slightly from publish/subscribe events because they are triggered once. These events are registered and then resolved using a Promise. No matter when you "subscribe" to the event, you'll receive a value whether the value was resolved previously or has yet to be resolved. Note, however, that these events are also published with pub/sub when resolved the first time, for any pub/sub subscribers of the event. (Pub/sub is the underlying event handling that drives Spokes.js.)

Each lifecycle has it's own pub/sub topic. Built-in lifecycle Page topic has events Loaded and QueryStringParsed. Lifecycles are registered by name (e.g. Page) and events like these are registered to that lifecycle. The default lifecycle for the page is encapsulated in new Page() and registered with a Lifecycle instance.

// As seen above, we can register a lifecycle like this, by
// giving it a name and providing a factory method to setup a
// `Lifecycle` instance by registering events (e.g. `Loaded`).
spokes.registerLifecycle('Page', lifecycle => new Page(document).load(lifecycle));

// Within that `registerLifecycle` method, the following types of things are done:

// You may want to know when the page is loaded. This can be called
// at any time and the value will be passed to the callback when it's
// available. This is different from subscribing to pub/sub because
// if you subscribe to a pub/sub topic too late, you may miss events.
// Note: `when` returns a Promise, for which you can call `then` or  `catch`.
spokes.when('Page', 'Loaded').then(document => console.log('Page:Loaded resolved'));

// Here's an example of registering a lifecycle event, such as telling other components
// when your SPA is loaded and  ready.
spokes.registerLifecycle('SPA', lifecycle => {
  lifecycle.registerEvent('Initialized', (resolve, reject) => {
    // Do whatever needs to be done to finish loading your SPA,
    // then trigger `resolve` to pass back a payload to subscribers.
    // If there is a failure, call `reject`.
    if (success) {
      resolve(/*...*/);
    } else {
      reject(/*...*/);
    }
  });
});

// Another ecommerce-related lifecycle event might be `CheckoutCompleted`,
// if it doesn't happen more than once per page load.
spokes.registerLifecycle('Ecommerce', lifecycle => {
  lifecycle.registerEvent('CheckoutCompleted', (resolve, reject) => { /*...*/ });
});