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

@ripetchor/ssm

v1.0.3

Published

Simple State Manager

Readme

Simple State Manager

A lightweight, type-safe state management library for JavaScript applications. It provides a simple store for managing global state with features like batched updates, selectors, subscriptions, and configurable flushing strategies.

Features

  • Type-Safe: Fully typed with generics for state and slices.
  • Batched Updates: Queues state updates and flushes them in batches to optimize performance.
  • Selectors: Extract specific slices of state without re-rendering the entire store.
  • Subscriptions: Subscribe to state changes with optional equality checks and immediate callbacks.
  • Flush Strategies: Control when updates are applied (sync, pre via microtask, or post via idle callback).
  • Immutable State: Uses structuredClone for deep copies to ensure immutability.
  • Destroy Method: Clean up subscribers and reset state when no longer needed.
  • Helper Functions: createStore for instantiation and createSelector for reusable selectors.

Usage

Basic Example

Create a store, set state, and subscribe to changes.

import { createStore } from "@ripetchor/ssm";

interface AppState {
  count: number;
  user: string;
}

const initialState: AppState = { count: 0, user: "guest" };

const store = createStore(initialState);

// Get current state
console.log(store.getState()); // { count: 0, user: 'guest' }

// Set state
store.setState({ count: 1 });

// Subscribe to a slice
const unsubscribe = store.subscribe(
  (state) => state.count,
  (newCount) => console.log(`Count updated: ${newCount}`),
);

// Update state again
store.setState((prev) => ({ count: prev.count + 1 }));

// Unsubscribe
unsubscribe();

// Destroy the store
store.destroy();

Advanced Usage

Selectors

Use selectors to derive computed values from the state.

import { createSelector } from "@ripetchor/ssm";

const countSelector = createSelector<AppState, number>((state) => state.count);

const selectedCount = store.select(countSelector);
console.log(selectedCount); // e.g., 2

Subscription Options

Customize subscriptions with equality functions and immediate callbacks.

store.subscribe(
  (state) => state.user,
  (newUser) => console.log(`User: ${newUser}`),
  {
    immediate: false, // Don't call callback immediately
    equalityFn: (a, b) => a === b, // Custom equality check
  },
);

Flush Options

Control when batched updates are flushed.

  • sync: Flush immediately (synchronous).
  • pre: Flush in the next microtask (default, using queueMicrotask).
  • post: Flush during idle time (using requestIdleCallback with 1s timeout).
store.setState({ user: "admin" }, { flush: "sync" });

API Reference

createStore<TState>(initialState: TState): Store<TState>

Creates a new instance of the Store class.

  • Parameters:

    • initialState: The initial state object (generic TState).
  • Returns: A Store instance.

Store<TState> Class

The core class for managing state.

Constructor

new Store(initialState: TState)

Initializes the store with a deep clone of the initial state.

Methods

  • getInitialState(): TState

    Returns the original initial state (read-only).

  • getState(): TState

    Returns the current state.

  • select<TSlice>(selector: SelectorFunction<TState, TSlice>): TSlice

    Applies a selector function to the current state and returns the selected slice.

    • Parameters:
      • selector: A function that takes the state and returns a slice.
  • setState(partial: ((prevState: TState) => Partial<TState>) | Partial<TState>, options?: SetStateOptions): void

    Queues a state update. The update can be a partial object or a function that returns a partial based on the previous state.

    • Parameters:
      • partial: Partial state or updater function.
      • options: Optional object with flush strategy ('sync' | 'pre' | 'post'). Defaults to { flush: 'pre' }.

    Updates are batched and applied during flush. Multiple updates in the queue are merged sequentially.

  • subscribe<TSlice>(selector: SelectorFunction<TState, TSlice>, callback: (nextState: TSlice) => void, options?: SubscribeOptions<TSlice>): () => void

    Subscribes to changes in a selected slice of state.

    • Parameters:

      • selector: Function to select the slice.
      • callback: Function called with the new slice when it changes.
      • options: Optional object:
        • immediate: Boolean (default true): Call callback immediately with current slice.
        • equalityFn: Function to compare previous and next slices (default Object.is).
    • Returns: An unsubscribe function.

    Subscribers are notified only if the selected slice changes according to the equality function.

  • destroy(): void

    Clears all subscribers, empties the update queue, and resets the current state to the initial state.

createSelector<TState, TSlice>(selector: SelectorFunction<TState, TSlice>): (state: TState) => TSlice

Creates a reusable selector function.

  • Parameters:

    • selector: The selector logic.
  • Returns: A function that can be passed to select or subscribe.

Types

The library exports several types from ./types:

  • SelectorFunction<TState, TSlice>: (state: TState) => TSlice
  • SetStateOptions: { flush: 'sync' | 'pre' | 'post' }
  • SubscribeOptions<TSlice>: { immediate?: boolean; equalityFn?: (a: TSlice, b: TSlice) => boolean }
  • Subscriber<TState, TSlice>: Internal type for subscribers.
  • UpdateFunction<TState>: (prev: TState) => Partial<TState>

Examples

Counter App

const store = createStore({ count: 0 });

const increment = () => store.setState((prev) => ({ count: prev.count + 1 }));

store.subscribe(
  (state) => state.count,
  (count) => console.log(`New count: ${count}`),
);

increment(); // Logs: New count: 1

With Custom Equality

For arrays or objects, provide a deep equality function.

import { deepEqual } from "some-deep-equal-lib"; // Assume imported

store.subscribe(
  (state) => state.items,
  (newItems) => console.log("Items changed"),
  { equalityFn: deepEqual },
);

Performance Considerations

  • Batched updates reduce unnecessary re-renders.
  • Use selectors to minimize subscription triggers.
  • structuredClone is used for immutability, which may have performance overhead for very large states. Consider shallow copies if deep cloning isn't necessary.
  • Flush strategies allow tuning for responsiveness vs. performance.

Limitations

  • No built-in middleware or effects (keep it simple).
  • Assumes modern JS environments; polyfill structuredClone, queueMicrotask, or requestIdleCallback` if needed for older browsers.

License

MIT