Skip to content

ui-router-effect ​

NPM VersionGitHub Release

ui-router-effect provides Effect bindings for a @uirouter/core router: a SubscriptionRef of its current state. It imports only effect and @uirouter/core, so any Effect program can follow route state without Lit. lit-ui-router-effect builds its Lit controllers on it and re-exports routeRef.

Installation ​

bash
npm install ui-router-effect effect

@uirouter/core and effect are peer dependencies.

Effect 3 and 4 ​

Both majors are supported (effect@^3.22.0 || ^4.0.2). routeRef is the same on either; the examples below are written for Effect 4. Two differences show up around it:

  • Effect 4 reads a ref's stream with SubscriptionRef.changes(ref); on Effect 3 it is the ref's own ref.changes property.
  • Effect 4's Equal.equals compares plain objects structurally. Effect 3 compares them by reference, so wrap a selected object in Data.struct(...) for Equal.equals to see two equal selections as unchanged.

routeRef ​

routeRef(router) returns a SubscriptionRef of the router's current route. The first call for a router creates its one ref and registers one onSuccess transition hook that sets it, so every value it holds is a settled route. Later calls return the same ref. The ref and the hook live as long as the router; nothing removes the hook.

Reading the ref once gives the route at that moment:

ts
import { Effect, SubscriptionRef } from 'effect';
import { routeRef } from 'ui-router-effect';

const program = Effect.gen(function* () {
  const route = yield* SubscriptionRef.get(routeRef(router));
  yield* Effect.log(route.current?.name);
});

Effect.runFork(program);

Its changes stream starts with the current route and emits once per successful transition. Stream.changes drops repeats of a primitive:

ts
import { Effect, Stream, SubscriptionRef } from 'effect';
import { routeRef } from 'ui-router-effect';

const names = SubscriptionRef.changes(routeRef(router)).pipe(
  Stream.map((route) => route.current?.name),
  Stream.changes,
);

Effect.runFork(Stream.runForEach(names, (name) => Effect.log(name)));

A selection that builds an object needs Stream.changesWith(Equal.equals) to drop repeats:

ts
import { Effect, Equal, Stream, SubscriptionRef } from 'effect';
import { routeRef } from 'ui-router-effect';

const active = SubscriptionRef.changes(routeRef(router)).pipe(
  Stream.map((route) => ({
    inbox: route.includes('inbox.**'),
    contacts: route.includes('contacts.**'),
  })),
  Stream.changesWith(Equal.equals),
);

Effect.runFork(Stream.runForEach(active, (sections) => Effect.log(sections)));

RouteSnapshot ​

The ref holds a RouteSnapshot, taken once per successful transition. snapshotRoute(router, transition?) builds one directly. Every field answers for the moment it was taken:

FieldValue
currentthe current state declaration
paramsthe current parameter values, a fresh object per transition
transitionthe transition that produced the snapshot; undefined before the first
includes(stateOrName, params?)StateService.includes against the snapshot, globs like 'a.**' included

includes() evaluates against the snapshot, not the live router, so a selector asking during a later transition gets the settled answer.

Further reading ​