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 🙏

© 2025 – Pkg Stats / Ryan Hefner

subscription-state

v2.0.0

Published

React state management

Readme

what is it

Yet another react state management micro lib.

why

Let's imagine we know nothing of react, and we just got a ticket to share some counter between two components in header and footer, our intuition will tell is something like this:

class MyState {
    
    counter = 0
    
    incrementCounter() {
        counter += 1
    }   
}

looking at class above you just immediately get your head around the code because it's pretty straightforward. And let's imagine that we use Mystate#counter in several components and wherever we call incrementCounter we expect our components to update. like:

const myState = new MyState()

const Foo = () => {
    return <div>
        {myState.counter}
        <button onClick={()=>{myState.incrementCounter()}}>
            increment
        </button>
    </div>
}

const Bar = () => {
    return <div>
        {myState.counter}
    </div>
}

although it would not work (but let's imagine it is working), for any programmer the thing above is self explainable, and if one would not even know React, he could read the code and just assume what will happen.

But unfortunately there is no such thing, to implement the minimal sample as above we'll have to do quite a work. Let's say we use redux for that, and for a single property it begins: we want to impress our colleagues don't we?: stores, reducers, hocs, writing hundreds lines, and etc. if we want anything async, or state depending on another state, or some synchronization between them, say goodbye to sanity and wish best of luck to whom will maintain it afterwards. (though in some cases such ceremony is ok and even necessary).

This lib allows you to do pragmatic state management and at the same time allow you to write it in any style you want.

How

just define your state as follows:

class Counter extends SubscriptionState {

    static instance = new Counter() // let's make a singleton for brevity.
    
    counter = 0
    
    incrementCounter() {
        this.counter += 1
        this.update() // will update all subscribed components
    }
    
    // async example
    async incrementCounterAsync() {
        this.counter = await SomeCounterService.increment()
        this.update() 
    }    

}

to make it work your state class should only do two things: extend from SubscriptionState, in any method you wish subscribed components to update - call this.update()

now lets rewrite the initial example:


const Foo = () => {
    const counterState = MyState.instance.use() // <=== that's the only thing you should do in your component to make it work
    return <div>
        {counterState.counter}
        <button onClick={()=>{counterState.incrementCounter()}}>
            increment
        </button>
    </div>
}
const Bar = () => {
    const counterState = MyState.instance.use() 
    
    return <div>
        {counterState.counter}
        <button
            onClick={()=>{counterState.incrementCounterAsync()}}
        >
            inrement async
        </button>
    </div>
}

and it will just work. Whenever you'll press a button all components that .use() your myState, will update.

With minimal intervention, you have explicit, easy to implement and to maintain state. You just write it in standard OOP way.

Recap, you call ${yourStateClassInstance}.use() in your component, and it will subscribe to the state. Whenever update() is called in your state class all the subscribed components will be updated. On unmount, component will be automatically be unsubscribed.

you may pass an instance around or have a singleton for "global" state

optimizations

lib handles the cases for nested subscribed components insuring that they will always be rendered once for each state version. So e.g. if you use context, and some of the child of context user is also uses same context, it be rendered for each time parent rendered, plus own renders, this lib excludes such cases.

for niche cases you can as well pass following options to have fine-grained control over the update

const myComp = () => {
    
    const myState = YourState.instance.use({
        makeSnapShot: (state)=>{return {counter: state.counter}}, // make snapshot of data used by this component, it will be yielded in next update and be made on each update
        shouldUpdate: (state, snapshot)=>{ // will yield last snapshot? made by makeSnapshot
            return state.counter !== snapshot.counter //: boolean, component will be updated only if true was returned
        },
        afterUnsubscribed = () => {
            //do here any cleanup or any logic you want to run when 
        }       
    })    
    return <div>foo {myState.foo}</div>
}

//all options are optional

almost real world example:

class UsersState extends SubscriptionState {
    
    users?: User[]    

    async loadUsers() {
        this.users = await User.fetchUsers()
        this.update()
    }
}

export const myStateInstance = new MyState()

const UsersIndex = () => {
    
    const myState = myStateInstance.use()

    useEffect(()=>{
        myState.loadUsers()
    }, [])
    
    if (!myState.users) {
        return <p>loading</p>
    }
    return <div>
        {myState.users.map((user)=>{
            <p key={user.id}>{user.name}</p>
        })}       
    </div>
}

const UserAddressIndex = () => {
    const myState = myStateInstance.use()

    return <div>
        {myState.users?.map((user)=>{
            <p key={user.address.id}>{user.address.city}</p>
        })}       
    </div>
}

in above example, after users loaded it will update both UsersIndex and UserAddressIndex

immutability

lib doesn't stand in your way and has no opinions how you use it at all.

E.g. it allows to not only have primitive objects in state, but instances of classes and etc.

as well if you want immutability, and e.g. state versioning aka time machine debugging0 :

export class SampleState extends SubscriptionState {

    data = {name: 'joe', id: 3}
    
    history = []
    
    setName(name: string) {
        this.history.push(this.data) // if you want e.g. versioning for debugging etc.
        this.data = {...this.data, ...{name}} //do it immutable way
        this.update()
    }
    
}

can i use it in prod?

if you feel adventurous.

lib was used in quite big SPAs with over 9000 hardcore state management related problems, so it's battle tested.

and it just fun to play around with it.

without comments and whitespace it's < 50 lines.

Bonus

if lib looks strange it includes a bonus ProvidableState which shares almost same interface and behaves almost same, but uses react's context underhood.

Absolutely no magic, just oopified context usage.

export class SampleProvidableState extends ProvidableState {

    name = "bar"

    setName(value: string) {
        this.name = value
        this.dispatch()
    }

}

const state = new SampleProvidableState()

const Foo = () => {
    state.use()
    return <p>state.name</p>    
}

const Bar = () => {
    <button
        onclick={()=>{state.setName("joe")}}
    >change</button>
}

const App = () => {
    return <providableState.provider>
        <Foo/>
    </providableState.provider>    
}

how it works

see SubscriptionState#use() doc comments

licence

MIT