31% less code: RxAngular vs StateAdapt 1. Todo MVC
Tags: angular, stateadapt, rxangular
Published: 2022-10-25 16:44:42

---
title: 31% less code: RxAngular vs StateAdapt 1. Todo MVC
published: true
description: I implemented Todo MVC with StateAdapt and the code decreased by 31%. 
tags: angular,stateadapt,rxangular
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/izzfx0wdiut2majxuwi8.png
# Use a ratio of 100:42 for best results.
# published_at: 2022-10-24 05:27 +0000
series: RxAngular vs StateAdapt
---

[YouTube video for this article](https://www.youtube.com/watch?v=1ylIsHi4gtc)

I implemented Todo MVC with [StateAdapt](https://github.com/state-adapt/state-adapt) and the code decreased by 31%. 

![Todo MVC UI](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ote68xmg7po4if2qvjes.png)

## State change logic

The main difference is how state change logic is defined in StateAdapt. Some may not like how compact it is. However, the compactness enables portability, so I like it. Here it is in StateAdapt:

```tsx
  todosAdapter = createAdapter<Todo[]>()({
    create: (todos, text: Todo['text']) => [
      ...todos,
      {
        id: Math.round(Math.random() * 100000),
        text,
        done: false,
      },
    ],
    remove: (todos, { id }: Todo) => todos.filter((todo) => todo.id !== id),
    update: (todos, { id, text, done }: Todo) =>
      todos.map((todo) => (todo.id !== id ? todo : { id, text, done })),
    toggleAll: (todos, done: Todo['done']) =>
      todos.map((todo) => ({ ...todo, done })),
    clearCompleted: (todos) => todos.filter(({ done }) => !done),
    selectors: {
      completed: (todos) => todos.filter(({ done }) => done),
      active: (todos) => todos.filter(({ done }) => !done),
    },
  });
```

In StateAdapt, you can define state management patterns for each type/interface, and combine adapters to manage the combined type/interface. Like this: 

```tsx
  adapter = joinAdapters<TodoState>()({
    filter: createAdapter<TodoFilter>()({ selectors: {} }),
    todos: this.todosAdapter,
  })({
    /**
     * Derived state
     */
    filteredTodos: (s) =>
      s.todos.filter(({ done }) => {
        if (s.filter === 'all') return true;
        if (s.filter === 'active') return !done;
        if (s.filter === 'completed') return done;
      }),
  })();
```

This takes the state changes and selectors for each child adapter and makes it available on the new parent adapter, by using TypeScript magic to prepend the names by its property name (`filter` or `todos`). 

This can create some naming awkwardness for selectors. The `todosAdapter`'s selector `completed` becomes available on the new adapter as `adapter.todosCompleted`, whereas natural English would have called it `adapter.completedTodos`. However, since this drills down from general to specific, there is something nice about it too. In any case, it's not a problem. 

More naming awkwardness can come from state change names. For now. Because I am working on a potential solution for this. But the issue is with plural vs singular. Since state change names are verbs, I put the first word first, then the namespace the state change came from, then the rest of the state change name. So `set` on a child adapter becomes `setFilter`, for example. `setToTrue` would become `setCheckedToTrue` if the namespace was `checked`. But in this example, we have `create`, as in, create a single todo item. This becomes `adapter.createTodos`, even though it only creates one. But in the future I plan on creating a list adapter, so this would become `addOne`, similar to NgRx/Entity (which was a primary inspiration for StateAdapt in the first place). 

Anyway, despite this awkwardness, it allows you to easily define compact, reusable state logic. 

Here is the same code in RxAngular:

```tsx
interface Commands {
  create: Pick<Todo, 'text'>;
  remove: Pick<Todo, 'id'>;
  update: Pick<Todo, 'id' | 'text' | 'done'>;
  toggleAll: Pick<Todo, 'done'>;
  clearCompleted: Pick<Todo, 'done'>;
  setFilter: TodoFilter;
}

// ...
  /**
   * UI actions
   */
  private readonly commands = this.factory.create();

  /**
   * State
   */
  private readonly _filter$ = this.select('filter');
  private readonly _allTodos$ = this.select('todos');

  /**
   * Derived state
   */
  private readonly _filteredTodos$ = this.select(
    selectSlice(['filter', 'todos'])
  ).pipe(
    map(({ todos, filter }) =>
      todos.filter(({ done }) => {
        if (filter === 'all') return true;
        if (filter === 'active') return !done;
        if (filter === 'completed') return done;
      })
    )
  );
  private readonly _completedTodos$ = this._allTodos$.pipe(
    map((todos) => todos.filter((todo) => todo.done))
  );
  private readonly _activeTodos$ = this._allTodos$.pipe(
    map((todos) => todos.filter((todo) => !todo.done))
  );

// ...
    /**
     * State handlers
     */
    this.connect('filter', this.commands.setFilter$);
    this.connect('todos', this.commands.create$, ({ todos }, { text }) =>
      insert(todos, {
        id: Math.round(Math.random() * 100000),
        text,
        done: false,
      })
    );
    this.connect('todos', this.commands.remove$, ({ todos }, { id }) =>
      remove(todos, { id }, 'id')
    );
    this.connect(
      'todos',
      this.commands.update$,
      ({ todos }, { id, text, done }) => update(todos, { id, text, done }, 'id')
    );
    this.connect('todos', this.commands.toggleAll$, ({ todos }, { done }) =>
      update(todos, { done }, () => true)
    );
    this.connect(
      'todos',
      this.commands.clearCompleted$,
      ({ todos }, { done }) => remove(todos, { done }, 'done')
    );
// ...
```

RxAngular is awesome because it puts RxJS first, unlike most other state management libraries. The ability to have in your state/store's declaration that it will react to an observable can really make state management cleaner. 

RxAngular is awesome.

## Callbacks

Another big difference is that I eliminated callback functions. RxAngular's implementation has this:

```tsx
  setFilter(filter: TodoFilter): void {
    this.commands.setFilter(filter);
  }

  create(todo: Pick<Todo, 'text'>): void {
    this.commands.create(todo);
  }

  remove(todo: Pick<Todo, 'id'>): void {
    this.commands.remove(todo);
  }

  update(todo: Todo): void {
    this.commands.update(todo);
  }

  toggleAll(todo: Pick<Todo, 'done'>): void {
    this.commands.toggleAll(todo);
  }

  clearCompleted(): void {
    this.commands.clearCompleted({ done: true });
  }
```

Here's what I changed that to in StateAdapt:

```tsx
  setFilter = this.store.setFilter;
  create = this.store.createTodos;
  remove = this.store.removeTodos;
  update = this.store.updateTodos;
  toggleAll = this.store.toggleTodosAll;
  clearCompleted = this.store.clearTodosCompleted;
```

Although it could have been similar in the RxAngular implementation, I decided not to do it this way, because StateAdapt is extremely committed to completely reactive code organization. Traditionally, callback functions like these were seen as a good practice because they allow future flexibility. But they only allow for imperative flexibility. Observables are already flexible, because anything can refer to them and be updated, as long as there is a declarative API for it. Read more about this philosophy [here](https://dev.to/this-is-angular/simple-derived-state-progressive-reactivity-in-angular-48oe#progressive-reactivity-rule-2).

This extreme commitment can make StateAdapt awkward to use when you're dealing with imperative APIs everywhere. But [the solution is to create declarative wrapper for those APIs](https://dev.to/this-is-angular/wrapping-imperative-apis-in-angular-550b). 

## Conclusion

For a full comparison, see [this PR comparison](https://github.com/edbzn/rx-angular-todo-mvc/compare/main...mfp22:state-adapt-todo-mvc:main?expand=1). 

RxAngular is still currently my top pick for state management in Angular. [StateAdapt](https://github.com/state-adapt/state-adapt) is still a work in progress. I need to apply it to many more projects before I can have the confidence to release version 1.0. If you think it has potential, I'd appreciate a star, and I'd love for you to try it out and share your thoughts.

Thanks!

## Links

[Repo](https://github.com/mfp22/state-adapt-todo-mvc)

[StateAdapt](https://github.com/state-adapt/state-adapt)

[RxAngular](https://www.rx-angular.io/)

[Twitter](https://twitter.com/mfpears)