StoreStack

Description

StoreStack is a simple state management library for JavaScript applications. It provides a way to create and manage state using the Observer pattern. The library is designed to be easy to use and understand, making it a great choice for developers who want a lightweight solution for state management.

Import

To use StoreStack in your project, simply import the library:

import { useState, Stores } from 'storestack';

Usage

Using useState

useState is the preferred method for managing state in StoreStack. It is a convenience method that combines creating, accessing, and updating a store. It is similar to the useState hook in React:

const [getState, setState] = Stores.useState({
  pointer: 'myPointer',
  defaultValue: 0,
});

console.log(getState()); // Output: 0
setState((prevState) => prevState + 1);
console.log(getState()); // Output: 1

Creating a store

To create a new store, use the useStore function. This function accepts a default state value and an optional options object:

const pointer = useStore(0, {
  onChange: (state) => console.log(state),
});

The useStore function returns a pointer to the created store. You can use this pointer to access the store and update its state.

Accessing a store

To access a store, use the Stores.get method. This method accepts a pointer and returns the store associated with that pointer:

const store = Stores.get(pointer);

Updating a store

To update the state of a store, use the store.set method. This method accepts a new state value or a function that modifies the current state:

store.set((prevState) => prevState + 1);

Observing a store

To observe a store, create an observer using the useObserver function. This function accepts a callback that will be called when the store's state changes:

const observer = useObserver((currentState, prevState) => {
  console.log('State changed from', prevState, 'to', currentState);
});

Then, attach the observer to the store using the store.attach method:

store.attach(observer);

You can also detach an observer from a store using the store.detach method:

store.detach(observer);

Using global observers

If you want to use global observers that apply to all stores, you can use the StoreStack.attach method:

const globalObserver = useObserver((currentState, prevState) => {
  console.log('Global observer:', currentState, prevState);
});

Stores.addGlobalObserver(globalObserver);

API reference

useState

useState is a convenience method that combines creating, accessing, and updating a store:

const [getState, setState] = Stores.useState({
  pointer: 'myPointer',
  defaultValue: 0,
});

console.log(getState()); // Output: 0
setState((prevState) => prevState + 1);
console.log(getState()); // Output: 1

useStore

  • state: T: The initial state of the store.
  • options?: StoreOptions<T>: Optional configuration options for the store.

StoreOptions

  • pointer?: Pointer: A unique pointer to the store location in window.stores.
  • onChange?: (state: T) => void: A callback that will be called when the store's state changes.
  • observers?: Observer<T>[]: Additional observers that should be attached to the store.
  • override?: boolean: If set to true, will override the store at the address if it exists.
  • errorHandling?: StoreOptionsErrorHandling: Defines how useStore should handle errors.

StoreOptionsErrorHandling

  • verbose?: boolean: Set to true if error messages should be outputted to the console.
  • stopOnError?: boolean: Set to true if the function should stop on error and re-throw the error.

useObserver

  • callback: (currentState: T, prevState?: T | null) => void: A callback that will be called when the store's state changes.

StoreStack

  • configure(): Checks if the StoreStack is instantiated on window.stores and creates it if it's not.
  • attach<T>(globalObservers?: Observer<T>[]): This method should only be called when the StoreStack is instantiated on the component level, and only in really specific circumstances globally when global observers are desirable.
  • addStore(newItem: AnyStore): Adds a store to the memory stack and assigns it a pointer.
  • addStoreAtPointer(newItem: AnyStore, pointer: Pointer, options?: {override?: boolean; verbose?: boolean}): Adds a store to the specified Pointer. If a store already exists at the address, an option can be passed to override it.
  • upsert<T>(defaultValue: T, pointer: Pointer, ...observers: Observer<T>[]): This method makes sure a store is instantiated at the Pointer's address. If no store is instantiated, it will create one holding the defaultValue provided. Otherwise, it will only attach Observer if they are provided.
  • removeStore(ptr: Pointer, options?: {verbose?: boolean}): Removes a store from the memory stack. If the Pointer's address is unallocated, it will output an error message to the console if verbose option is set to true.
  • get<T = any>(ptr: Pointer): Returns a reference to the store at the address if it exists, otherwise undefined. A type can be passed in order to make the return typed.
  • addGlobalObserver<T>(...globalObservers: Observer<T>[]): Adds global observers to the stack.

Examples

Using useState method

import { Stores } from 'storestack';

const [getState, setState] = Stores.useState({
  pointer: 'myPointer',
  defaultValue: 0,
});

console.log(getState()); // Output: 0
setState((prevState) => prevState + 1);
console.log(getState()); // Output: 1

Creating and using a store

import { useStore, Stores, useObserver } from 'storestack';

const pointer = useStore(0, {
  onChange: (state) => console.log(state),
});

const store = Stores.get(pointer);
store.set(1); // Output: 'State changed: 1'

Observing a store

import { useStore, Stores, useObserver } from 'storestack';

const pointer = useStore(0);
const store = Stores.get(pointer);

const observer = useObserver((currentState, prevState) => {
  console.log('State changed from', prevState, 'to', currentState);
});

store.attach(observer);
store.set(1); // Output: 'State changed from 0 to 1'

Now you should have a good understanding of how to use StoreStack in your projects to manage state. The library provides a simple and lightweight solution for state management and makes it easy to create, update, and observe state changes in your application.