react-event-storm

React wrapper for event-store library

Usage no npm install needed!

<script type="module">
  import reactEventStorm from 'https://cdn.skypack.dev/react-event-storm';
</script>

README

Publish

React event store

A small React wrapper for in memory event store. The provided API is with hooks.

Technical stack

  • React ^16.8.0(hooks support)

API

useStorm useStorm provides a simple hook that wraps the store's subscribe method. It will automatically unsubscribe the component from the store changes on unmount.

What is a Subscription

Generally saying subscription is receiving some information(maybe something) over some agreement. In the case of computer science, there are some patterns based on this logic(mainly it is popular in event-driven design systems). As you can expect the library is providing a subscription functionality to allow subscription on store segments information.

The problem

Imagine a react application. You have a component and it is subscribed to store. It is using sizes and cards properties. The cards are used to render a list. Let's imagine you want to save in backend the provided sizes for clicked card. So, the sizes information is used whenever you are clicking on a particular card. In most store managers(e.g. Overmind, Redux) your component will be updated on sizes change even if the user will never click to the list. To avoid unnecassary renders, you'll need to directly access the store in your card's click event handler. Which is most probably not the solution you're looking for. With the workarround you'll get store usage in a "react-way" as usual, also in a "none react-way"(accessing the store diretly, not via useStorm or some other hook). Also, worth nothing that you'll have components where you need more than one handler. So you'll need to duplicate the code that accesses the store.

The Solution

The event-storm is presenting 3 levels of subscription(via useStorm):

  • Active subscription
  • Passive subscription
  • Condition-based subscription

Active Subscription

This is a regular subscription. Whenever you are using any key from the store like in the example below, you'll receive any update on that keys.

import { createStore } from 'event-storm';
import { useStorm } from 'react-event-storm';

const store = createStore({
  taxes: 20,
  grossSalary: 100_000,
});

function AnyComponent() {
  const { taxes } = useStorm(store);

  return (
    <div>{taxes}</div>
  );
}

This is the same as:

import { createStore } from 'event-storm';
import { useStorm } from 'react-event-storm';

const store = createStore({
  taxes: 20,
  grossSalary: 100_000,
});

function AnyComponent() {
  const { taxes } = useStorm(store, { active: true });

  return (
    <div>{taxes}</div>
  );
}

Passive subscription

This option allows you to access any store key without getting you component rerendered on the particular keys' updates. It is guaranteed that whenever you'll use the store values they'll be up to date(fresh values).

import { createStore } from 'event-storm';
import { useStorm } from 'react-event-storm';

const store = createStore({
  taxes: 20,
  grossSalary: 100_000,
});

function AnyComponent() {
  const { taxes } = useStorm(store, { active: false });

  const onClick = () => {
    console.log(taxes);
  }

  return (
    <button onClick={onClick}>Click me</button>
  );
}

Condition-based subscription

What is a condition-based subscription? The updates on the keys will not update the component until the provided condition will be truthy. Whenever active option can becomes true during runtime, the subcription becomes active. This means, starting from that point, your component will be rerendered on the key's updates. With this feature, you are controlling your subscription.

import { useState } from 'react';
import { createStore } from 'event-storm';
import { useStorm, usePublish } from 'react-event-storm';

const store = createStore({
  taxes: 20,
  grossSalary: 100_000,
});

function App() {
  const [loading, setLoading] = useState(true);
  const { cards, sizes } = useStorm(store, { active: !loading });

  return loading ? <Loading /> : cards.map(card => <Card sizes={sizes} />);
}

You can change the value of active option from true to false or vise versa. Whenever it'll be false your component will not be updated for the used keys' changes.

NOT RECOMMENDED: Do not use the store as an argument to hooks dependency. RECOMMENDED: Use store segments instead.


import { createStore } from 'event-storm';
import { useStorm } from 'react-event-storm';

const store = createStore({
  taxes: 20,
  grossSalary: 100_000,
});

function AnyComponent() {
  const { taxes } = useStorm(store);

  /* The following way of using the store is not recommended. Each render will update this effect.
    useEffect(() => {
      console.log(store.taxes);
    }, [store]);
  */

 // correct usage
  useEffect(() => {
    console.log(taxes);
  }, [taxes]);

  return (
    <div>{taxes}</div>
  );
}

Updating the store

usePublish

import { createStore } from 'event-storm';
import { useStorm, usePublish } from 'react-event-storm';

const store = createStore({
 taxes: 20,
 grossSalary: 100_000,
});

function AnyComponent() {
 const { taxes } = useStorm(store);
 const publish = usePublish(store);

 return (
   <div onClick={() => publish({ taxes: 30 })}>
     {taxes}
   </div>
 );
}

Advanced usage

You can also subscribe to exact models you want to

import { createModel } from 'event-storm';
import { useModels } from 'react-event-storm';

const isPopupVisible = createModel(true);
const age = createModel(1);

function AnyComponent() {
  const [popupVisible, ageValue] = useModels([isPopupVisible, age]);
  return (
    <div>
      {isPopupVisible ? 'close' : 'open'}
      {age}
    </div>
  );
}

Usefull tips

The provided hooks will work for any store instance. Some boilerplate for wrapping it with a store instance to avoid importing the store everywhere:

import { useStorm as useBaseStorm, usePublish as useBasePublish } from 'react-event-storm';
import { createStore } from 'event-storm';

const defaultState = {
  posts: [],
  user: null,
  comments: [],
  isLoggedIn: ({ user }) => !!user,
};

const store = createStore(defaultState);

export const useStorm = options => useBaseStorm(store, options);
export const usePublish = () => useBasePublish(store);
export default store;

Playground

Examples: