Options
All
  • Public
  • Public/Protected
  • All
Menu

Module @lauf/store

Lightweight Application State Management

codecov

Logo - Image of Runner

Lauf Store

Logo - Diego Naive, Noun Project.

Install

npm install @lauf/store --save

@lauf/store provides a minimal reactive state-management solution, a simple substitute for Flux/Redux based on Immer.

It is incredibly lightweight and suitable for adoption with almost any server-side or client-side framework in Typescript or Javascript.

Framework-independent async bindings for @lauf/store are provided by @lauf/store-follow.

React bindings for @lauf/store are provided by @lauf/store-react.

Browse the API or see the minimal JS and TS examples inlined below without React or Async, showing the fundamentals of defining a new application state, tracking changes and making edits.

In Javascript

const { createStore } = require("@lauf/store");

// Create and initialize a store
const store = createStore({
  roses: "red",
  violets: "blue",
});

// Watch for changes
store.watch(console.log);

// Change the color - this change will automatically call console.log in the next tick, producing
// { roses: 'white', violets: 'blue' }
store.edit((draft) => {
  draft.roses = "white";
});

In Typescript

import { createStore, Immutable } from "@lauf/store";

// Define a type for Store state
export type AppState = Record<string, string>;

// Define the initial Store state
const INITIAL_STATE: Immutable<AppState> = {
  roses: "red",
  violets: "blue",
} as const;

// Create and initialize a store
const store = createStore(INITIAL_STATE);

// Watch for changes
store.watch(console.log);

// Change the color - this change will automatically call console.log in the next tick, producing
// { roses: 'white', violets: 'blue' }
store.edit((draft) => {
  draft.roses = "white";
});

Visit @lauf/store-react to learn about useSelected() which can refresh React components when only a selected part of your state changes.

Index

Type aliases

Editor

Editor<T>: (draft: Draft<Immutable<T>>, castDraft: CastDraft) => void

Type parameters

  • T

Type declaration

    • (draft: Draft<Immutable<T>>, castDraft: CastDraft): void
    • A function passed to Store.edit. The editor is called back with a draft - a mutable proxy of the Store's current Immutable RootState.

      You can make changes to the mutable draft proxy within your editor callback using any javascript syntax. When it returns, Immer efficiently composes a new Immutable state to reflect your drafted changes, leaving the old state intact. The new state is passed to Store.write.

      The editor is equivalent to Immer's producer except returning a value doesn't replace the RootState. To replace the state call Store.write instead of using an editor. This eliminates Immer's runtime errors when you draft changes as well as returning a value, (easily done by accident in simple arrow functions).

      For careful use in rare cases, Immer's castDraft is available in the second editor argument. It can cast parts of previous Immutable states to be 'mutable' for assignment to the next draft state. Thos items can be added to the draft state, but no changes should actually be made to them.

      See Immer docs for more detail on the conventions for Immer producers.

      Parameters

      • draft: Draft<Immutable<T>>

        A mutable proxy of a Store's existing Immutable state, used to compose the next state.

      • castDraft: CastDraft

        Unsafely casts Immutable references to mutable to use them in the next draft.

      Returns void

Immutable

Immutable<T>: T extends (...args: any[]) => any ? T : T extends object ? ImmutableIndex<T> : T

Recursive implementation of Typescript's Readonly<T>.

Unlike some implementations of immutability this approach introduces no special objects and methods and typically doesn't require you to change your code.

It is used to flag and enforce immutability of a RootState and its descendants - values assigned to a Store (Store.write) or retrieved from it (Store.read). The type Immutable<T> is equivalent to applying Readonly<T> to T and its descendant properties, telling the compiler that no change should be made anywhere in a Store's state tree.

Relying on Typescript's builtin Readonly allows the use of normal javascript values and syntax, with the exception that operations which would manipulate the item are disallowed by the compiler. Applications written in Typescript get the greatest benefit from this approach, but javascript IDEs that load typings for code-completion can also indicate violations of the Readonly contract.

Primitive properties are already immutable by definition. Functions are treated as primitive values. All other objects and arrays have their children made Readonly recursively.

Type parameters

  • T

PartitionableState

PartitionableState<Key>: RootState & {[ k in Key]: RootState }

An item satisfying type constraints of RootState but where a child item at Key also satisfies RootState. A Store with a PartitionableState can therefore be partitioned into a child Store by Key.

Partitioning enables hierarchy and logical isolation of a Store, so that higher-level stores can be composed of multiple lower-level stores. Logic relying on some Store<T> need not know whether <T> is the whole app state or just some part of it.

Partitioning can also make eventing more efficient. When a parent Store's RootState changes, implementations can omit notifications for all Watchers of a child partition if the child RootState has not changed, meaning no value within the child partition has changed.

See also createStorePartition.

Type parameters

  • Key: string | number | symbol

RootState

RootState: object

Defines the set of possible state types for a Store, usually the top level State 'container' is either an Array, Tuple, or keyed Object

Selector

Selector<State, Selected>: (state: Immutable<State>) => Immutable<Selected>

Type parameters

Type declaration

Unwatch

Unwatch: () => void

Type declaration

    • (): void
    • Handle returned from Watchable.watch that can disable an earlier subscription.

      Returns void

Watcher

Watcher<T>: (item: T) => unknown

Type parameters

  • T

Type declaration

    • (item: T): unknown
    • Function to be subscribed through Watchable.watch to be notified of an item T.

      Parameters

      • item: T

      Returns unknown

Functions

createStore

  • createStore<State>(initialState: Immutable<State>, watchers?: ReadonlyArray<Watcher<State>>): Store<State>

createStorePartition

  • createStorePartition<State, Key>(store: Store<State>, key: Key, watchers?: ReadonlyArray<Watcher<State[Key]>>): Store<State[Key]>
  • Constructs a Store that tracks a child property of another store's RootState. See PartitionableState for more details.

    Type parameters

    Parameters

    • store: Store<State>

      The parent store containing the partition

    • key: Key

      The child key to partition the parent's state.

    • Optional watchers: ReadonlyArray<Watcher<State[Key]>>

      A list of Watchers to be notified once and permanently subscribed

    Returns Store<State[Key]>

    The partitioned store.

Generated using TypeDoc