0.5.5 • Published 4 years ago

@jas-chen/evstore v0.5.5

Weekly downloads
-
License
MIT
Repository
github
Last release
4 years ago

build status

evstore

npm version gzip size

Event based state management library.

  • Framework agnostic
  • Code-splittable stores
  • One place to update state, makes it easy to debug
  • Stores can be freely combined
  • Powered by mitt

Install

yarn add @jas-chen/evstore

The UMD build is also available on unpkg:

<script src="https://unpkg.com/mitt/dist/mitt.umd.js"></script>
<script src="https://unpkg.com/@jas-chen/evstore/dist/evstore.umd.js"></script>

You can find the library on window.evstore.

Usage

It's an event emitter just like mitt

import evstore from '@jas-chen/evstore';

const container = evstore.create();

// listen to an event
container.on('foo', e => console.log('foo', e) )

// listen to all events
container.on('*', (type, e) => console.log(type, e) )

// fire an event
container.emit('foo', { a: 'b' })

// working with handler references:
function onFoo() {}
container.on('foo', onFoo)   // listen
container.off('foo', onFoo)  // unlisten

However you can create stores and store values on it

container.register('year', 2020);
container.get('year'); // 2020

Update and watch a store

container.on('time', console.log);

container.register(
  'time',
  new Date(), // initial value
  (setState, getState) => {
    setInterval(() => setState(new Date()), 1000);
  },
);

Once a store is registered, it cannot be emited from outside

container.emit('time', 12345); // throws an error

API

TBD


evstore-react

npm version gzip size

React binding for @jas-chen/evstore

Install

yarn add @jas-chen/evstore-react

Counter example

import React from 'react';
import ReactDOM from 'react-dom';
import evstore from '@jas-chen/evstore';
import { Provider, useContainer, useStore } from '@jas-chen/evstore-react';

const container = evstore.create();

container.register('count', 0, (setState, getState) => {
  container.on('increment', () => setState(getState() + 1));
  container.on('decrement', () => setState(state => state - 1));
});

const Counter = () => {
  const container = useContainer();
  const count = useStore(container, 'count');

  return (
    <>
      <button onClick={() => container.emit('increment')}>+</button>
      {count}
      <button onClick={() => container.emit('decrement')}>-</button>
    </>
  );
}

const App = () => (
  <Provider value={container}>
    <Counter />
  </Provider>
);

ReactDOM.render(<App />, document.getElementById('root'));

Timer example

import React from 'react';
import ReactDOM from 'react-dom';
import evstore from '@jas-chen/evstore';
import { Provider, useContainer, useOn } from '@jas-chen/evstore-react';

const container = evstore.create();

setInterval(() => container.emit('tick'), 1000);

const Timer = () => {
  const container = useContainer();
  const [time, setTime] = React.useState(() => new Date().toLocaleString());
  useOn(container, 'tick', () => setTime(new Date().toLocaleString()));

  return time;
}

const App = () => (
  <Provider value={container}>
    <Timer />
  </Provider>
);

ReactDOM.render(<App />, document.getElementById('root'));

Todo example

import React from 'react';
import ReactDOM from 'react-dom';
import evstore from 'evstore';
import { Provider, useContainer, useStore } from 'evstore-react';

const container = evstore.create();

container.register('todos', [], (setState, getState) => {
  let id = 0;

  container.on('ADD_TODO', (label) => {
    setState(
      getState().concat({
        id: id++,
        label,
        completed: false,
      })
    );
  });

  container.on('DELETE_TODO', (id) => {
    setState(getState().filter((todo) => todo.id !== id));
  });

  container.on('TOGGLE_TODO', (id) => {
    setState(
      getState().map((todo) => {
        if (todo.id === id) {
          return { ...todo, completed: !todo.completed };
        } else {
          return todo;
        }
      })
    );
  });
});

container.register('filter', 'ALL', (setState) => {
  container.on('CHANGE_FILTER', setState);
});

container.register('filteredTodos', container.get('todos'), (setState) => {
  const updateFilterTodos = () => {
    const filter = container.get('filter');
    const todos = container.get('todos');
    setState(
      filter === 'ALL'
        ? todos
        : todos.filter((todo) => todo.completed === (filter === 'COMPLETED'))
    );
  };

  ['todos', 'filter'].forEach((key) => {
    container.on(key, updateFilterTodos);
  });
});

const TodoMVC = () => {
  const inputRef = React.useRef();
  const container = useContainer();
  const todos = useStore(container, 'filteredTodos');
  const filter = useStore(container, 'filter');
  React.useEffect(() => inputRef.current.focus(), []);

  return (
    <>
      <form
        onSubmit={(e) => {
          e.preventDefault();
          container.emit('ADD_TODO', inputRef.current.value);
          e.target.reset();
        }}
      >
        <input ref={inputRef} placeholder="What needs to be done?" />
      </form>
      <ul>
        {todos.map(({ id, label, completed }) => (
          <li key={id}>
            {label}
            <input
              type="checkbox"
              checked={completed}
              onChange={() => container.emit('TOGGLE_TODO', id)}
            />
            <button onClick={() => container.emit('DELETE_TODO', id)}>
              remove
            </button>
          </li>
        ))}
      </ul>
      {['ALL', 'ACTIVE', 'COMPLETED'].map((filterKey) => (
        <label key={filterKey}>
          <input
            type="radio"
            checked={filter === filterKey}
            onChange={() => container.emit('CHANGE_FILTER', filterKey)}
          />
          {filterKey}
        </label>
      ))}
    </>
  );
};

const App = () => (
  <Provider value={container}>
    <TodoMVC />
  </Provider>
);

ReactDOM.render(<App />, document.getElementById('root'));

Browser Support

This package uses Map, Set and Symbol, you may need polyfills for legacy browsers.

License

MIT License

0.5.4

4 years ago

0.5.5

4 years ago

0.5.3

4 years ago

0.5.2

4 years ago

0.5.1

4 years ago

0.5.0

4 years ago

0.4.0

4 years ago

0.3.1

4 years ago

0.3.0

4 years ago

0.2.1

4 years ago

0.2.0

4 years ago

0.1.0

4 years ago