When the Account Menu feature in Angular Movies was rebuilt with StateAdapt, the codebase shrank by 52%.
Another round of nested state
Just like in the prior feature, RxAngular was holding state as { loggedIn: boolean }, while StateAdapt got by with a plain boolean.
What about effects?
The real win came from dropping unnecessary side-effects. The RxAngular version carried these:
this.effects.register(this.ui.signOut$, this.authEffects.signOut);
this.effects.register(
this.ui.signIn$,
this.authEffects.approveRequestToken
);
That meant the template was dispatching through ui.signOut$.next($event) and ui.signIn$.next($event), when it could invoke authEffects.signOut and authEffects.approveRequestToken directly. Granted, I still added thin class properties as a bridge, because many Angular developers prefer templates that don't reach straight into services. In the StateAdapt version, that cost only two lines:
signOut = this.authEffects.signOut;
signIn = this.authEffects.approveRequestToken;
This also meant this piece of the RxAngular implementation could simply disappear:
type Actions = {
signOut: Event;
signIn: Event;
};
Imports, providers, and the constructor
RxAngular demanded more imports and providers, which made the constructor noticeably heavier. Stripping those away brought it down from
constructor(
private authEffects: AuthEffects,
private authState: AuthState,
private state: RxState<{ loggedIn: boolean }>,
private effects: RxEffects,
private actionsF: RxActionFactory<Actions>
) {
down to
constructor(private authEffects: AuthEffects, private authState: AuthState) {}
StateAdapt only brought in two imports: adapt and booleanAdapter. Now, adapt itself needs a provider, but that provider is only set up once per application, since a global store resembling Redux is created behind the scenes. So adapt still needs injection. My own preference is to use inject rather than stuffing providers into the constructor, which works especially well for libraries.
Common ground
RxAngular's connect method is something I genuinely enjoy. StateAdapt turns out to have nearly the same concept. Compare this excerpt from RxAngular
this.state.connect(
'loggedIn',
this.authState.accountId$.pipe(map((s) => s !== null))
);
with the StateAdapt equivalent
accountIdTruthy$ = this.authState.accountId$.pipe(
map((s) => s !== null),
toSource('accountIdTruthy$')
);
loggedIn = adapt(['loggedIn', false, booleanAdapter], this.accountIdTruthy$);
With that setup, this.accountIdTruthy$ feeds into the set method of booleanAdapter (or whatever adapter you hand over). If you had a property called isLoggedIn, you could take control of it with something like { setIsLoggedIn: this.accountIdTruthy$ }.
All told, RxAngular and StateAdapt sit very close to each other. They're the most reactive state management libraries I've come across for Angular, a point I covered in an earlier piece: Choosing a State Management Library for Progressive Reactivity in Angular.
Wrapping up
If you'd like to see the full comparison, check out the commit here.
From here on, the Angular Movies features are going to get more involved. My guess is RxAngular will start closing the gap in code size. Also, minimalism is nice for readability, but it's not the only way to judge a library. RxAngular has gained real momentum, its docs are solid, and the support around it is strong, which is why it's still my first choice for Angular state management. StateAdapt is still maturing. I want to run it through many more real-world scenarios before I'm comfortable shipping a 1.0. If you believe in it too, a star would mean a lot, and I'd love to hear what you think if you give it a spin.
Appreciate the read!
Update: The Angular Movies codebase has a few rough edges at the moment, so I'm going to take a small detour and try some conversions from NGXS and NgRx. That said, I'm already looking forward to jumping back into RxAngular code before too long!

