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

@zuze/stateful

v4.1.2

Published

Ridiculously tiny state/selector package

Downloads

28

Readme

@zuze/stateful

npm version Coverage Status Build Status Bundle Phobia

What is this?

It's a ridiculously tiny and highly performant state mangement solution when you don't want to implement redux. It's supposed to be minimalistic (comes in most handy for a source of state for library developers) and extremely simple. It comes with everything you need (including a super tiny memoized selector implementation) to maintain your state.

Getting Started

Install it as a dependency in your JavaScript/Typescript project

npm install @zuze/stateful
# or
yarn add @zuze/stateful

Or just pull it in from the browser:

<script src="https://unpkg.com/@zuze/stateful"></script>
<script>
  const { state } = stateful;
  const myState = state('jim!');
  myState.subscribe(console.log); // jim!
</script>

API

state(initialState: T): Stateful<T>

Create a stateful instance with an initial state. Returns the stateful interface:

  • getState(): T Returns the current state.

  • setState((state: T) => T): void Can be used set state using a function

    import { state } from '@zuze/stateful';
    
    const s = state({ fetching: false, error: false });
    s.setState(state => ({ ...state, fetching: false, data: 'some data' })); // { fetching: false, error: false, data: 'some data' }
    import { state } from '@zuze/stateful';
    
    const s = state({ fetching: false, error: false });
    s.setState(state => ({ ...state, fetching: false, data: 'some data' })); // { fetching: false, error: false, data: 'some data' }
  • subscribe(subscriberFunction: Subscriber<T>): Unsubscribe Register a subscriber function to be notified every time the state changes (see selectors). Returns an unsubscribe function.

    const s = state('jim');
    const unsub = s.subscribe(console.log); // logs jim
    s.setState(() => 'fred'); // logs fred
    unsub();
    s.setState(() => 'bill'); // nothing logged

createSelector(...selectors, combiner)

The purpose of a selector (popularized in reselect) is to minimize expensive computations through memoization.

There is an alternate method for using selectors outside of minimizing expensive computations: because the combiner function only gets called when at least one of it's arguments change, it essentially becomes a callback for changes in the input selectors.

import { createSelector, state } from '@zuze/stateful';

const myFetchingSelector = createSelector(
    ({ fetching }) => fetching,
    (fetching) => {
        console.log("fetching changed",fetching);
    }
);

const s = state({
    fetching: false,
    data: null,
    error: true;
});

s.subscribe(myFetchingSelector); // logs "fetching changed",false

(async() => {selectors
    s.setState(state => ({ ...state, fetching: true })); // logs "fetching changed",true

    try {
        const data = await someAPICall();
        s.setState(state => ({ ...state, data })) // not called!
    } catch {
        s.setState((state => ({ ...state, error: true })) // not called!
    }

    s.setState((state => ({ ...state, fetching: false })); // logs "fetching changed",false
});

memo(fn)

Bare bones memoization implementation. You aren't allowed to mess with the comparator

import { memo } from '@zuze/stateful';

const myFunc = (...someArgs) => {
  // ... some expensive computations
  return 42;
};

const memoed = memo(myFunc);
console.log(memoed(...someArgs)); // outputs 42 - expensive computations performed
console.log(memoed(...someArgs)); // outputs 42 - expensive computations skipped!

About Selector Memoization

There are 2 levels of memoizations going on when creating a selector.

  1. The function returned from createSelector itself is memoized using checker
  2. The combiner function is memoized using checker.

What this effectively means is:

  1. If the function returns from createSelector is called with the same arguments neither the input selectors nor the combiner will be called
  2. If the function returned from createSelector is called with different arguments, all input selectors will be called. If these executions result in the same arguments as the last time, the combiner will NOT be called.