The "DeRxJSViewModel Pattern": The E=mc^2 of State Management [Part 2]
Tags: rxjs, react, angular, javascript
Published: 2021-10-31 07:58:17

---
title: The "DeRxJSViewModel Pattern": The E=mc^2 of State Management [Part 2]
published: true
description: Creating simple presentational components from our domain-agnostic DeRxJS ViewModel code.
tags: rxjs, react, angular, javascript
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y69zi8zela4vqt38ztnd.png
---

👋 hi all - this article is a continuation of a [previous article](https://dev.to/zackderose/the-derxjsviewmodel-pattern-the-e-mc-2-of-state-management-part-1-3dka) that explains the goals of the DeRxJS pattern:

+ entirely de-couple state management code from presentational code (to the point where your state management code could be re-used across frameworks)
+ maximize the benefits of RxJS, while minimizing the negatives
+ next-level testing [and potential to hand over all our state-management code to AI at some point]

In that article, we did most of the heavy lifting - developing our state-management code and fully testing out every edge case with "timeline testing" (allowing us to assert not only "what" state looks like, but "when" it should look that way as well).

In this article, we'll bring that state management code and show how we can use this across 3 front-end "frameworks": React, Angular, and Vanilla JS.

## React

One of the goals of DeRxJS [as the name suggests] is to remove actual RxJS code from our code bases. We discussed in the previous example how [@derxjs/reducer](https://www.npmjs.com/package/@derxjs/reducer) can help us write our state management, leveraging RxJS, but without actually writing any RxJS code ourselves.

![@derxjs/react logo](https://github.com/ZackDeRose/derxjs/raw/main/derxjs-react-logo.png)

In this article, I'm excited to introduce [@derxjs/react](https://www.npmjs.com/package/@derxjs/react) - that will allow us to leverage that same approach to our react presentation code.

In all honesty, I've prioritized React as the first presentation-based package for derxjs in part because of React's popularity. But beyond that there are 2 huge reasons that I've targeted React first:

1. RxJS and React don't really play well together [yet!]
2. One of my favorite things about React is how it is not domain-specific, for nearly everything but state-management! (Interestingly, I think this is almost entirely inverted from Angular, which I'd argue is domain-specific for everything BUT state-management) Marrying React and RxJS I think can close that gap, so that the state-management code you write is as domain-agnostic as the rest of your react code.

In general, I'm quite long on React. My motivations are almost entirely selfish - I think if this package works the way I want it to, this will be my ideal environment for frontend development.

Without further ado, here's the general api for our @derxjs/react code:

```ts
export const TicTacToe = () => {
  return DeRxJSComponent<
    TicTacToeViewModelInputs,
    TicTacToeViewModel,
    TicTacToeProps
  >({
    viewModel$: ticTacToeViewModel$,
    component: TicTacToeView as any,
    initialValue: createInitialViewModel(),
    triggerMap: {
      spaceClick: 'userSpaceClickEvents$',
      resetClick: 'userResetClickEvents$',
    },
    inputs: {
      ai: randomAi,
    },
  });
};
```

+ `viewModel$` is imported from our work from the previous article
+ `component` is a presentational component (we'll see that next!)
+ `initialValue` is the starting value for our state (the `createInitialViewModel()` function comes from the previous article as well)
+ `triggerMap` is a [type-safe!] object that maps the name of "trigger" functions for our presentational components to Observable inputs of our `viewModel$`. "Trigger" functions are how we'll communicate the message passing our presentation component will need to perform, and hand this off to to the @derxjs/react package to turn those into Observables (so we don't have to write any of that RxJS code ourselves).
+ `inputs` is our way to provide any non-reactive (or non-Observable) inputs to our `viewModel$` function. Note we're passing our `randomAi` function here - essentially parameterizing functionality of our `viewModel$` this way. (Would be fun in future work to create an "unbeatable" ai as well!)

This API is designed to allow you to write all presentational code as "presentational" components, delegating any smarts to your @derxjs/view-model, and using the provided trigger functions for message passing.

Here's how that code ends up looking:

```ts
interface TicTacToeProps {
  spaceClick: (spaceCoordinates: SpaceCoordinates) => void;
  resetClick: () => void;
}

interface SpaceProps {
  contents: SpaceContent;
  spaceCoordinates: SpaceCoordinates;
  clickHandler: (spaceCoordinates: SpaceCoordinates) => void;
}
const Space = ({ contents, clickHandler, spaceCoordinates }: SpaceProps) => (
  <div>
    <button onClick={() => clickHandler(spaceCoordinates)}>
      {contents.toUpperCase()}
    </button>
  </div>
);

function TicTacToeView({
  state,
  triggers,
}: {
  state: TicTacToeViewModel;
  triggers: TicTacToeProps;
}) {
  return (
    <>
      <h2>{state.turn}</h2>
      <div className={'border'}>
        <div className={'board'}>
          {([0, 1, 2] as const)
            .map((row) => ([0, 1, 2] as const).map((column) => [row, column]))
            .flat()
            .map(([row, column]) => (
              <Space
                contents={state.board[row][column]}
                spaceCoordinates={{ row, column }}
                clickHandler={triggers.spaceClick}
                key={`${row}:${column}`}
              />
            ))}
        </div>
      </div>
      <button className="reset" onClick={triggers.resetClick}>
        Reset
      </button>
    </>
  );
}
```

Note how `onClick`s are set to those "trigger functions" we defined.

Here's that code in action:

{% stackblitz react-ts-6wxgfx %}

## Angular

Next up: Angular! As mentioned, I'm of the opinion that Angular is generally very domain-agnostic when it comes to state management. In particular, it's very RxJS friendly.

As such, I don't know if a @derxjs/angular package is really necessary. Eventually we could end up creating a package of utilities for hiding more of the RxJS code that we'd write, but I have no plans for that at the moment.

Here's a look at the Typescript component code we'll write:

```ts
export class AppComponent {
  userResetClickObserver!: Observer<void>;
  userResetClickEvents$ = new Observable<void>(
    (observer) => (this.userResetClickObserver = observer)
  );
  userSpaceClickObserver!: Observer<SpaceCoordinates>;
  userSpaceClickEvents$ = new Observable<SpaceCoordinates>(
    (observer) => (this.userSpaceClickObserver = observer)
  );
  vm$ = ticTacToeViewModel$({
    ai: randomAi,
    userSpaceClickEvents$: this.userSpaceClickEvents$,
    userResetClickEvents$: this.userResetClickEvents$,
  });
  rows: BoardIndex[] = [0, 1, 2];

  handleSpaceClick(coordinates: SpaceCoordinates) {
    this.userSpaceClickObserver.next(coordinates);
  }

  handleResetClick() {
    this.userResetClickObserver.next();
  }
}
```

Note how we're creating our observables by creating an 'observer' property on the class at "construction time", and then in our click handler methods, we call `next()` on those observers. (This essentially the same "message passing" as our React code, but the @derxjs/react package hid most of the actual code here)

Similar to our react example, we'll see the same idea of a 'presentational' component in our template - with the one exception of passing our `viewModel$` to the Angular async pipe at the top level of our template:

```html
<h1>Tic Tac Toe</h1>
<ng-container *ngIf="vm$ | async as vm">
  <h2>{{ vm.turn }}</h2>
  <div class="border">
    <div class="board">
      <ng-container *ngFor="let row of rows">
        <div *ngFor="let column of rows">
          <button (click)="handleSpaceClick({ row, column })">
            {{ vm.board[row][column] | uppercase }}
          </button>
        </div>
      </ng-container>
    </div>
  </div>
  <button class="reset" (click)="handleResetClick()">Reset</button>
</ng-container>
```

Nice and simple :). Here's the stackblitz for our Angular code:

{% stackblitz angular-ivy-qe6zzv %}

## Vanilla JS

In this example, we'll use the [dom-manipulation API](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Manipulating_documents) to do the lifting that React and Angular were doing in their examples. Here's the simplified version of what we're doing:

1. Create a "template" for our component, attaching id's to the buttons we'll need to listen to for clicks/update their text. This example is a bit fortunate as all the elements on the DOM are static (they don't need to be added or removed, so we can just leave the `Element` objects on the DOM as-is, and change their text content. This would be significantly more difficult if this were not the case).
2. use [`fromEvent`](https://rxjs.dev/api/index/function/fromEvent) from RxJS to get observables of the 'click' events on the buttons.
3. Once we have our observables, pass them to that same `viewModel$` function we used in React and Angular to create our View Model observable.
4. Subscribe to that observable, and update the 'board' by changing the text content of the buttons to match the `board` property on the view model object.

Here's how that looks in the stackblitz:

{% stackblitz typescript-bavrh2 %}

## Conclusion

And that's a wrap!! Hope that this article helped spark some cool ideas. Be sure to reach out on [twitter](https://twitter.com/zackderose) or check out [the DeRxJS repo](https://github.com/ZackDeRose/derxjs) if you ever want to jam about state-management or good code architecture!!

## About the author

![Zack DeRose](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/idzdgnshhxtbabgvvrmw.jpg)

Zack DeRose [or DeRxJS if you like] is:
+ [a GDE in Angular](https://developers.google.com/community/experts/directory/profile/profile-zack-derose?hl=en)
+ a recent nx conf/NgConf/RxJS Live/The Angular Show/ZDS speaker
+ Creator of the [@derxjs OSS packages](https://github.com/ZackDeRose/derxjs)
+ Senior Engineer and Engineering Manager at [Nrwl](https://nrwl.io/)

Checkout out my [personal website](https://zackderose.dev/videos) for more of my dev content! And go bug [Jeff Cross](https://twitter.com/jeffbcross)/[Joe Johnson](https://twitter.com/joerjohnson) if you want to hire me to come help out your codebase or come help level up your team on Nx/NgRx/DeRxJS/RxJS/State Management! (I especially love building awesome stuff - and building up teams with bright developers that are eager to learn!)