Skip to content

aimtbr/use-app-events

Repository files navigation

use-app-events

NPM Version NPM Unpacked Size Code test coverage Libraries.io dependency status for latest release

  • Create, trigger and listen for custom events in vanilla JavaScript and React.
  • Create the reactive global variables in React instead of a complex state and cumbersome context.

📦 Small package (~22 kB)
🍃 Tree-shakeable
📝 Well documented
🛡️ Strictly typed with TypeScript
♻️ Events interact with each other across different browser tabs
⚛️ Exports a convenient API for React developers


Table of contents


Installation

npm

npm install use-app-events

pnpm

pnpm add use-app-events

API

  • useGlobal 🆕
    • Create a reactive global variable in React.
    • For example, as a replacement for React context (createContext)
  • useAppEvents
    • Hook for managing application events in React.
  • notifyEventListeners
    • Function to notify all listeners of the specified event type(s) subscribed via listenForEvents.
  • listenForEvents
    • Function to subscribe and listen for the specified event type(s) to occur in the app.
  • listenForEvents.once
    • Function to subscribe and listen for the specified event type(s) to occur in the app only once.
  • heap
    • (readonly) Collection of resources operated by the package.
  • options
    • Collection of options used to adjust the behavior of the package.

Examples

Imports

Normal

import { useGlobal } from 'use-app-events';

Selective (tree-shakeable)

import notifyEventListeners from 'use-app-events/notifyEventListeners';

State 🆕

Create a reactive global variable in React 🆕

  1. The initial value of the variable is retrieved from the older instances of useGlobal if any (can be turned off by disabling the synchronize option, see below), otherwise your initial value is used.
    // Turning off the `synchronize` option
    const [value, updateValue] = useGlobal('value', undefined, { synchronize: false })
  2. The value is broadcasted to other browser tabs every time it's updated, meaning the value is the same in all tabs of your app serving as sessionStorage in some way for as long as there are multiple tabs open.

1. Storing a primitive

// Global variable name - 'theme'
// Initial value - 'light'
const [theme, updateTheme] = useGlobal('theme', 'light');

// 1. Re-render with the new "dark" value
// 2. Save the theme value globally
updateTheme('dark');

2. Storing an object

const [params, updateParams] = useGlobal('params', {
  color: 'white',
  palette: 'dark',
});

// Color will be updated in the original object
updateParams({ color: 'black' });

// Or get a previous value (just like setState in useState)
updateParams((prevValue) => ({
  ...prevValue,
  color: prevValue.palette === 'dark' ? 'black' : 'white',
}));

Events

Create your own custom events

1. Listen for an event

listenForEvents('media-resume', () => {
  // do something when the event is triggered
});

2. Emit an event

notifyEventListeners('media-resume');

3. Listen for an event (it will only be processed once here)

listenForEvents.once('load-resource', async (url) => {
  await fetch(url);
});

4. Emit an event with some data

notifyEventListeners(
  'load-resource',
  'https://www.npmjs.com/package/use-app-events',
);

5. Listen for multiple events

const unlisten = listenForEvents(['event-1', 'event-2'], (eventType, url) => {
  if (eventType === 'event-1') {
    // do something when 'event-1' is emitted
  }

  if (eventType === 'event-2') {
    // do something when 'event-2' is emitted
  }
});

6. Stop listening for events

unlisten();

7. Emit multiple events with some data

notifyEventListeners(
  ['event-1', 'event-2'],
  'https://www.npmjs.com/package/use-app-events',
);

[ More examples ]


How to use

  1. Create a reactive global variable in React 🆕 a. Create a hook for global theme management

    import { useGlobal } from 'use-app-events';
    
    export const useTheme = () => {
      const [theme, updateTheme] = useGlobal('theme', 'light');
    
      return {
        theme,
        updateTheme,
      };
    };
    
    // Use the hook in your component to get or update the theme globally
    const { theme, updateTheme } = useTheme();
    
    updateTheme('dark');
  2. Listen for events in React

    import { useAppEvents } from 'use-app-events';

    a. Listen for an event

    const Component = () => {
      const { listenForEvents } = useAppEvents();
    
      listenForEvents('event-A', (payload) => {
        // 1. Do something when 'event-A' is triggered...
        // 2. Process a payload if you expect it to be sent by `notifyEventListeners`
      });
    };

    b. Listen for multiple events

    const Component = () => {
      const { listenForEvents } = useAppEvents();
    
      listenForEvents(['event-A', 'event-B'], (eventType, payload) => {
        // 1. Do something when either 'event-A' or 'event-B' is triggered...
    
        // 2. Process a specific event by its type in `eventType`
        if (eventType === 'event-A') {
          console.log('We got an event A with some data', payload);
        }
      });
    };
  3. Notify the event listeners in React

    a. Trigger an event

    const Component = () => {
      const { notifyEventListeners } = useAppEvents();
    
      // Notify the listeners of this event type with no data
      notifyEventListeners('event-A');
    };

    b. Trigger an event with some data

    const Component = () => {
      const { notifyEventListeners } = useAppEvents();
    
      const payload = { a: 1, b: 2 };
    
      // Notify the listeners of this event type and give them some data
      notifyEventListeners('event-A', payload);
    };

    c. Trigger multiple events with some data at once

    const Component = () => {
      const { notifyEventListeners } = useAppEvents();
    
      const payload = { a: 1, b: 2 };
    
      // Notify the listeners of these event types and give them some data
      notifyEventListeners(['event-A', 'event-B'], payload);
    };

    d. Trigger multiple events with some data, but don't broadcast

    const Component = () => {
      const { notifyEventListeners } = useAppEvents();
    
      const payload = { a: 1, b: 2 };
    
      // Notify the listeners of this event type in the current tab only
      notifyEventListeners('event-A', payload, false);
      // Notify the listeners of these event types in the current tab only
      notifyEventListeners(['event-B', 'event-C'], payload, false);
    };
  4. Adjust options

    import options from 'use-app-events/options';
    import notifyEventListeners from 'use-app-events/notifyEventListeners';

    a. Disable event broadcasting globally

    options.broadcast = false;
    
    // From now on, notifyEventListeners will send events to the listeners of the current tab only by default
    notifyEventListeners('event-A', 'some-payload');

    b. Enable the debug mode globally

    options.debug = true;
    
    // From now on, listenForEvents and notifyEventListeners will output additional console logs on different stages
    listenForEvents('event-A', () => {});
    notifyEventListeners('event-A', 'some-payload');

TypeScript

  • It is recommended to have a list of event types that can be used in your app, for example, enum called EventType, and pass it to useAppEvents() for type safety and misprint-proof: EventType passed to useAppEvents as a type


    This way you are protected from the unexpected event types...


    Unacceptable type passed as the event type to listenForEvents


    ...and only allowed to use the expected ones.


    The expected allowed event type passed to listenForEvents The expected allowed event type passed to notifyEventListeners


  • However, if EventType is not provided, any string or enum can be used: Plain string passed as the event type to listenForEvents and notifyEventListeners


Use cases


Try it yourself

Clone the repository, install dependencies, and run the dev script to start a web app and play around with examples.

pnpm

git clone https://github.com/aimtbr/use-app-events.git && cd use-app-events && pnpm install && pnpm dev

npm

git clone https://github.com/aimtbr/use-app-events.git && cd use-app-events && npm install && npm run dev

Motivation

The motivation to create use-app-events was to find a way to manage the state from any part of the app (globally) and allow all elements to communicate with each other regardless of their position in the tree.

License

MIT

Author

Maksym Marchuk

About

Create, trigger and listen for custom events in vanilla JavaScript and React.

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors