Skip to content

mfellner/valtio-inversify

Repository files navigation

valtio-inversify 🪬

Build Status Codecov Build Size Version Downloads

Create valtio state from class-based stores using Inversify

valtio-inversify consists of TypeScript decorators and a middleware for Inversify. It allows valtio stores...

  • to be declared as injectable classes
  • to declare actions as class methods
  • to define subscriptions as (static) class methods
  • to create derived properties with acessor methods

Existing valtio utilities can still be used like usual.

import { valtioProxyMiddleware, valtio } from "@mfellner/valtio-inversify";

@valtio()
@injectable()
class CounterStore {
  count = 0;

  increment() {
    this.count += 1;
  }
}

const container = new Container();
container.applyMiddleware(valtioProxyMiddleware);

const counter = container.get(CounterStore);

Motivation

This library builds on top of the officially documented pattern for using classes as stores (to organize actions).

valtio-inversify enables an object-oriented approach to state management and aims to make state management code more maintainable and testable.

For instance, external dependencies like API clients can simply be injected into stores with the help of inversify.

valtio-inversify was inspired by the work on valtio-factory. If using Inversify is not an option for your own use case, perhaps valtio-factory would be a better fit as it aims to solve similar problems as this library.

Define actions

Actions are declared as methods on the class.

Subscribe

Subscribe to the whole state with instance methods

A class method annoted with @subscribe() will be subscribed to any state changes.

import { subscribe, valtio } from "@mfellner/valtio-inversify";

@valtio()
@injectable()
class CounterStore {
  count = 0;

  increment() {
    this.count += 1;
  }

  @subscribe()
  private onUpdated() {
    console.log("current count:", this.count);
  }
}

Subscription methods can be declared private or protected because they shouldn't be called externally.

Subscribe to a key with static methods

@subscribe takes an optional key argument to subscribe the annotated method only to state changes on the given key.

The annotated method will receive the updated value as an argument.

Subscriptions by key can be declared as static methods.

@valtio()
@injectable()
class CounterStore {
  @subscribe({ key: "count" })
  static onCountUpdated(value: number) {
    // called when `count` changes
  }

  count = 0;
}

Options

  • @subscribe<T> – For increased type safety, @subscribe can take an optional type argument.
  • @subscribe<T>({key?: keyof T}) – The annotated method will be subscribed to this key using valtio/utils/subscribeKey.
  • @subscribe({notifyInSync?: boolean}) – The subscription will be updated synchronously.

⚠️ Unsubscribe not supported

There's currently no way to unsubscribe a subscription.

Derive properties with accessor methods

Accessor ("getter") methods annoated with @derived() can return derived properties based on the current state. The methods are internally wapped with valtio/utils/derive.

import { derived, valtio } from "@mfellner/valtio-inversify";

@valtio()
@injectable()
class CounterStore {
  count = 0;

  increment() {
    this.count += 1;
  }

  @derived()
  get squaredCount() {
    return this.count * this.count;
  }
}

Options

  • @derived({sync?: boolean}) – This optional parameter is passed on to valtio's derive utility function.