[aurelia-store] how we use it

why

posting this in reference to https://github.com/aurelia/store/issues/68

how we use aurelia-store

having tried injecting the store and setting up subscriptions manually as well as using the connectTo decorator, we found both methods lacking (boilerplate and too much convention) and decided to dumb it down, this is what we use currently.
it is not perfect but it is contained, somewhat typed and somewhat simple. it does not cover all the edge-cases but so far it works for our use case.

actions

this is an action, both functions are exported for testing-purposes but only the one wrapped in dispatchify is used elsewhere.

export const _addStatistic = (old: State, s: IStatistic) => {
  const fn = (n: State) => {
    n.statistics.push(s)
  }
  return produce(old, fn) // produce being immer
}

export const addStatistic = dispatchify(_addStatistic)

dispatchify is pretty much the same as dispatchify from aurelia-store but it registers the action if it’s not registered yet.
using the name property when registering the action isn’t ideal but we have not yet found a use for properly named actions.

export const dispatchify = <T, P extends any[]>(r: Reducer<T, P>) => {
  const store: Store<State> = Container.instance.get(Store)
  if (!store.isActionRegistered(r as any))
    store.registerAction(r.name, r as any)
  return (...params: P) => store.dispatch(r as any, ...params)
}

using actions then comes down to importing the dispatchified function and calling it:

import { addStatistic } from 'actions/add-statistic'

await addStatistic({ category: 'what', subcategory: 'ever' })

subscriptions

for subscribing to state changes we use this function:

export const subscribe = <T>(
  u: keyof typeof f,
  fn: (a: T) => void,
): Subscription => {
  const store: Store<State> = Container.instance.get(Store)
  return store.state
    .pipe(
      pluck(...f[u]),
      distinctUntilChanged(),
    )
    .subscribe(fn);
}

where f is an object with keys with names corresponding to properties on the state. this somewhat odd solution was done to have all untyped stuff in one place (as opposed to all the places you call subscribe()). a solution with keyof State would be preferable if you could someohow type sub-properties as well.

const f = {
  label: ['label'],
  label_coordinate: ['label', 'coordinate'],
}

using subscribe:

import { subscribe, Subscription } from 'state/util'
import { IStatistic } from 'interface/statistic'

export class MyClass {
  sub: Subscription

  bind() {
    this.sub = subscribe<IStatistic[]>(s => {
      // updated, typed IStatistic[]
    })
  }

  unbind() {
    this.sub.unsubscribe()
  }
}
8 Likes

I really like what you did with the auto registration and simplification of dispatching actions. It makes it less verbose, but if you know you’re dealing with actions then explicitly needing to dispatch them in your app is a non-issue.

I like this. I might try it out with a project I am currently working on. Less typing is better, in my opinion. I really hate typing.