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

stama

v1.0.1

Published

An asynchronous and flexible state manager

Downloads

2

Readme

Stama JS

An asynchronous and flexible state manager/machine.

Installation

yarn add stama

Usage

Simple

import State from 'stama';

class CounterState extends State {
  static initState = { count: 0 }

  increase = () => this.modState({ count: v => v + 1 })

  decrease = () => this.modState({ count: v => v - 1 })

  reset: () => this.setState({ count : 0 })
}

const counter = new CounterState();

counter.subscribe(console.log);
// prints { count: 0 }

counter.increase();
// prints { count: 1 }

Advanced

import State from 'stama';

class DataState extends State {
  static initState = { loading: false, items: [] }

  static deriveProps(state) {
    let totalValue = 0;
    state.items.forEach(item => totalValue += item.value;
    return { totalValue };
  }

  loadData = async url => {
    await this.setState({ loading: true });
    const data = await fetch(url);
    return this.setState({ loading: false, data })
  }
}

const state = new DataState();

state.subscribe(console.log);
// prints { loading: false, data: null, totalValue: 0 }

state.loadData('endpoint/test');
// prints:
//   { loading: true, data: null }
//   { loading: false, data: [...], totalValue: 12.. }

Motivation

There are probably more state managers than there are stars in the sky. Since I like stars, I decided to create another one.

This state manager takes much inspiration from unstated, which in turn mimics the React way of setState.

Details

State class

The State class is a base class adding state management capabilities to subclasses. Just as with React state, the state object should not be modified without calling setState or modState. Therefore the .state property is read only. However, it is impossible to prevent accidentally modifying the inner properties of the state. Therefore, the State class offers, next to setState, a more powerful modState function that makes immutable state changes a lot easier.

static initState

If the initial state of the subclass doesn't depend on any inputs, it's easiest to define this static property on the class. The State class will set its initial state to this value, potentially with derived props if specified.

[static] deriveProps(state)

If any properties need to be derived from the state, it is possible to define a static or instance method called deriveProps that receives the latest state, and can return an object containing any derived properties.

class ExampleState extends State {
  static initState = { value: 5 }

  static deriveProps(state) {
    return { squaredValue: state.value * state.value };
  }
}

It is also possible to define a static or instance property with the same name that contains an object describing for each derived property how it is calculated.

class ExampleState extends State {
  static initState = { value: 5 };

  static deriveProps = { squaredValue: state => state.value * state.value };
}

constructor([initState])

Any subclass of State can pass its initial state by calling super(initState) in its constructor. This will immediately set the provided initial state, potentially with derived props if specified.

.state

Returns the current state of a State instance.

.stateStream

Returns an RxJS Observable emitting every new state.

.subscribe(onNext, [onError], [onComplete])

Convenience method for .stateStream.subscribe(...)

setState(updater, [callback]) (Promise)

Updates the current state according to the updater, and then calls the callback function. The updater can be an object describing the new state, which then will be directly set. Or, it can be an update function taking the current state and returning a new one.

setState returns a Promise that resolves once the state has been updated, the callback has been called, and the stateDidChange method has been called.

modState(updater, [callback]) (Promise)

Modifies the current state according to the updater, and then calls the callback function. The updater can be an object describing how the state should be modified.

modState is a powerful way to modify parts of existing state. Here are some examples:

class ExampleState extends State {
  static initState = {
    value: 5,
    foo: {
      bar: 12,
      other: 33
    }
  }

  increaseValue = () => this.modState({ value: v => v + 1 })

  increaseBar = () => this.modState({ foo: { bar: v => v + 1 })
  
  addValueToBar = () => this.modState({ foo: { bar: (v, state) => v + state.value } })
  
  addBarToValue = () => this.modState({ value: (v, state) => v + state.foo.bar })
  
  addBarToOther = () => this.modState({ foo: { other: (v, _, foo) => v + foo.bar } })
  
  changeAll = () => this.modState({
    value: (v, state) => v + state.foo.bar,
    foo: {
      bar: 43,
      other: (v, _, foo) => v + foo.bar
    }
  })
}

modState returns a Promise that resolves once the state has been updated, the callback has been called, and the stateDidChange method has been called.

stateDidChange(newState, oldState)

A utility method that is called on every state change. It's probably not wise to change state in this method.

.forceStateUpdate()

Sometimes it is unavoidable to have objects in the state that update themselves. If these changes need to retrigger some state calculation in the current State class, then forceStateUpdate should be called everytime any relevant object changes it state. Basically, it does not change the direct state, but since nested objects may have changed it will again apply deriveState on the current state, which may get updated property values.

.clone()

Creates a new instance of the current State class with the exact same state.