@sophosoft/nano-state
TypeScript icon, indicating that this package has built-in type declarations

0.3.0 • Public • Published

nano-state

Efficient, effective, and extensible state management: less is more.

installation

npm i @sophosoft/nano-state
# or
yarn add @sophosoft/nano-state

usage

nano-state is built from the ground-up with TypeScript to leverage strong typing. The target ECMAScript version is es5. While the examples below may be contrived, they should illustrate normal usage.

state

Extend the State type to define interfaces for your various states:

import { State } from '@sophosoft/nano-state'

interface UserState extends State {
    displayName: string
    email: string
    id?: string
    visits: number
}

context

A Context provides exclusive managment of state. Implement getters and mutation methods as necessary in the context.

import { Context } from '@sophosoft/nano-state'

class UserContext extends Context<UserState> {
    public constructor() {
        // initialize default state
        super({
            displayName: 'Guest',
            email: '',
            visits: 0
        })
    }

    public get DisplayName(): string {
        return this.state.displayName
    }

    // ...

    public login(id: string, name: string, email: string, visits: number): void {
        this.state.displayName = name
        this.state.email = email
        this.state.id = id
        this.state.visits = visits + 1
        this.notify('User:LoggedIn', { id: this.state.id })
    }

    public async cache(manager: CacheManager): Promise<void> {
        await manager.write('user', this.state)
        this.notify('User:Cached')
    }

    // ...
}

Note the usage of this.notify(string, payload?). This is the internal event system that is described below.

subscription

The Subscription abstract class allows middleware to react to one or more events. The contract includes one async handle(event: Event<EventPayload>) method, however a strategy pattern may be used to handle multiple events when necessary. By default, a subscription will listen to all events using the wildcard (*) event id. Events are described next.

import { Event, EventPayload, Subscription } from '@sophosoft/nano-state'

class LogSubscription extends Subscription {
    public async handle(event: Event<EventPayload>): Promise<void> {
        console.log(event)
    }
}
import { Subscription } from '@sophosoft/nano-state'

class AuthNSubscription extends Subscription {
    public constructor() {
        super(['AuthN:LogIn', 'AuthN:LogOut'])
    }

    public async handle(event: AuthNEvent): Promise<void> {
        if (event.id === 'AuthN:LogIn') {
            return this.handleLogIn(event)
        } else if (event.id === 'AuthN:LogOut') {
            return this.handleLogOut(event)
        }
    }

    // ...
}

event

Extend the Event and EventPayload types to define specific event payloads.

Events contain an id, store, and an optional payload. The Store is descibed next and may be used by subscriptions to access various contexts and trigger additional events. By default, the payload may be undefined, which will cause syntax errors if payload properties are accessed without proper typing.

import { Event, EventPayload } from '@sophosoft/nano-state'

interface UserLoginPayload extends EventPayload {
    id: string
    name: string
    email: string
    visits: number
}

interface UserLoginEvent extends Event<UserLoginPayload> {
    payload: UserLoginPayload
}

store

The Store class provides access to all of the contexts in an application and dispatches events to all subscriptions. It is intended to be a singleton, globally accessible throughout an application.

Contexts are mapped by aliases, which allow third-party libraries to be integrated with domain-specific names. In order to minimize the inital overhead of registering multiple Contexts, a factory method can be used to lazy-load a Context only when it is requested via Store.getContext(). In this way, a Context is not instanciated until it's needed, and then only once: The resulting instance is stored for subsequent requests.

When an event is triggered, subscriptions are notified in the order in which they were registered.

import { Store, ContextMap, Subscription } from '@sophosoft/nano-state'

// perhaps imported from a config file
const contexts: ContextMap = {
    'authn': new AuthNContext(),
    'user': () => new UserContext(), // lazy-loading
    // ...
}

// perhaps also imported
const subscriptions: Subscription[] = [
    new LogSubscription(),
    new AuthNSubscription(),
    // ...
]

class AppStore extends Store {
    public static readonly Instance: AppStore = new AppStore()

    private constructor() {
        super(contexts, subscriptions)
    }
}

Using the store within an application is then relatively trivial.

import { AppStore } from '@/store/AppStore'
import { UserContext } from '@/user/UserContext'

const store: AppStore = AppStore.Instance

const user: UserContext = store.getContext<UserContext>('user')

console.debug(`${user.DisplayName} is the current user`)

store.trigger('AuthN:LogOut', { id: user.Id })

Package Sidebar

Install

npm i @sophosoft/nano-state

Weekly Downloads

0

Version

0.3.0

License

MIT

Unpacked Size

16.8 kB

Total Files

8

Last publish

Collaborators

  • texdc