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

@leocode/rxstores

v0.1.1

Published

Rx.js based state manager for JS apps

Downloads

3

Readme

RxStores

Modular state manager for TS/JS apps, based on RxJS and focused on performance.

Prerequisites

  • Basic knowledge of RxJS

Installation

npm

npm i @leocode/rxstores

Yarn

yarn add @leocode/rxstores

Type definitions are built-in.

Main concepts

Store

Store acts as an observable data source for any view or view-model part of an app. A single store serves single data model. You can define your own interface here. It may be either only a facade for some complex business logic, or a container for some part of the logic as well.

By design, stores will expose two main properties:

  • data of RxJS Observable type
  • methods with logic-only interface exposed

One can use the logic interface either in the store instance itself, or using the methods property just for convention sake. See the example below.

Context

Context is a named container for stores. By default, the global one is used. One can select a custom one - see the example below.

Provider

Provider is a single, app-wide mechanism that exposes getters for stores and custom contexts.

Usage

Simple example

The simplest store one can create looks like this:

// stores/some.store.ts

import { Store } from "@leocode/rxstores";

export class SomeStore extends Store {
  init() {} // must be defined!
}

At this point, we have created the definition of SomeStore. Notice the init function, which is called right after the store is created. You might want to put here some initialization logic, e.g. API data fetching. Every store should implement initialization logic, even if empty.

Then, it can be used this way:

// index.js/ts

// define a callback function that will be called every time store's state changes
function onDataChange(data) {
  console.log("New data!", data);
}

// get a store from the global context
const store = Provider.getStore(SomeStore);
// or from a custom context
const store = Provider.from("some custom context").getStore(SomeStore);

// subscribe to the store with a callback function
const subscription = store.data$.subscribe(onDataChange);

// get store's current value using `value` property getter
const currentValue = store.value;

// emit a new value using `value` property setter
store.value = "some different value";

Provider lets us get a store directly from the global context, or specify a custom one with .from('context name').

This implementation doesn't do much, though. There's no initial data and no methods that could change the state. Let's fix this.

Defining a data model and initial value

TypeScript

// stores/some.store.ts

// define a model for data stored within the store below
export interface SomeModel {
  superImportantValue: number;
}

// create a store by extending base Store class and provide model type
export class SomeStore extends Store<SomeModel> {
  constructor() {
    const initialValue: SomeModel = { superImportantValue: 5 };

    // let base Store know that you want to provide an initial value
    super(initialValue);
  }

  init() {}
}

Now, we have provided an initial value for SomeStore. If initial value fetching logic is asynchronous, one can put it inside init method.

...

export class SomeStore extends Store<SomeModel> {
  ...

  // we can use async keyword here
  async init() {
    const remoteData = await fetch('/super-important-data')
      .then(r => r.json());
    this.value = remoteData;
  }
}

The issue is that view controllers are not able to know whether the data is loading, errored while loading, or is just non-existent. We can use a model helper class called Loadable.

import { Loadable } from '@leocode/rxstores/helpers';

...

export class SomeStore extends Store<SomeModel> {
  constructor() {
    super(Loadable.ofLoading<SomeModel>());
  }

  // we can use async keyword here
  async init() {
    try {
      const remoteData = await fetch('/super-important-data')
        .then(r => r.json());
      this.value = Loadable.ofData(remoteData);
    } catch (e) {
      this.value = Loadable.ofError(e);
    }
  }
}

Defining the logic interface

// stores/some.store.ts

export class SomeStore extends Store<SomeModel> {
  constructor() {
    super({ superImportantValue: 5 });
  }

  init() {}

  generateNewValue() {
    const newValue = Math.round(Math.random() * 10);

    this.value = newValue;
  }

  // all the methods go here
}

Accessing the interface

// index.js/ts

const someStore = Provider.getStore(SomeStore);

someStore.generateNewValue();
// or
someStore.methods.generateNewValue();

API

Store

| Name | Description | isRequired | | ------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | ---------- | | constructor | Here you can pass an initial value for your class, like user object | no | | init | Here goes all magic with creating your store. Put here code which has to be run at first, like downloading data, etc. | yes | | (get) value | This getter returns the value stored in this specific store, you don’t have to declare it. It is inside, just use it with this.value | yes | | (get) methods | This returns all method you declare in your class, this can return an empty object if you don’t have any methods | no | | (get) data$ | This is your Observable, you should subscribe to it if you want to have a value up to date | yes | | (set) value | Set data with this function in your class implementation | no |

Roadmap

While heads can be full of ideas, this library aims to be as modular and performant as possible. Thus, new features will be added to the project when it is obvious that no core rule is broken.

Current ideas include:

  • [ ] React bindings
  • [ ] Optional persistence