🐶 Intro to Recks: Rx+JSX experiment
Tags: rxjs, react, javascript, webdev
Published: 2019-12-03 15:21:16

I like React. And I love RxJS. So I tried to mix them in a new framework:


```jsx
import { timer } from 'rxjs';

function App() {
  const ticks$ = timer(0, 1000);

  return <div>
    <h1>{ ticks$ }</h1>
    <p>seconds passed</p>
  </div>
}
```
<figcaption><a href="https://codesandbox.io/s/recks-example-timer-fjyvj?fontsize=14&hidenavigation=1&theme=dark&module=/src/App" target="_blank">run this example</a></figcaption>

## tl;dr

[Github repo](https://github.com/recksjs/recks) 🙂

## Foreword

I've built this rendering engine in about a week for a hackathon. It turned out to be an interesting concept, that I wanted to share with you here!

## The concept

React made DOM "first-class citizen" in our JS code (via virtual DOM). We can create vDOM anywhere in our structure and then pass it around. 
React's components are basically a mapping of properties to vDOM:

```ts
// React
(props: Object) => vDOM
```

Angular deeply integrated Observable streams and made them native to its components and services. Observables let us easily operate and coordinate async events and updates, spread in time.

In this framework, we (similarly to React) map properties to vDOM. Only here we fully control update and render streams. We take the input stream of props and map them to the output stream of vDOM:

```ts
// This framework
(props$: Observable<Object>) => Observable<vDOM>
```

Stream in. Stream out.

![everything is a stream](https://thepracticaldev.s3.amazonaws.com/i/thm31yfqb5t1qlk2uz6d.jpeg)
<figcaption>Beloved dog from André Staltz' <a href="https://gist.github.com/staltz/868e7e9bc2a7b8c1f754">great article</a></figcaption>


Let's get to examples, shall we?

### Basic usage

Surely, we have to start with a "Hello World":

```jsx
import { of } from 'rxjs';

function App() {
  return of(<h1>Hello world!</h1>)
}
```

_[`of`](https://thinkrx.io/rxjs/of/) creates an Observable that emits a single provided value_

Since our component renders a static `<h1>` and never updates it — we can skip the Observable part and simply return the element:

```jsx
function App() {
  return <h1>Hello world!</h1>
}
```

Looks react-ish, doesn't it? Let's add more life to our components:

### A Timer

```jsx
import { timer } from 'rxjs';
import { map } from 'rxjs/operators';

function TimerApp() {
  return timer(0, 1000).pipe(
    map(tick =>
      <div>
        <h1>{ tick }</h1>
        <p>seconds passed</p>
      </div>
    )
  )
}
```

_[`timer(n, m)`](https://thinkrx.io/rxjs/timer/) emits a `0` at `n` and then will emit consequent integers with `m` interval_

Again our component returns a stream of vDOM. Each time a component emits a value — the vDOM is updated.

In this example, `timer` will emit a new value every second. That value we will `map` to a new vDOM, displaying each `tick` in the `<h1>`.

We can do this even simpler!

If a child in the vDOM is an Observable itself — the engine will start listening to it and render its values in place. So let's move the `timer` Observable right into the `<h1>`:

```jsx
import { timer } from 'rxjs';

function TimerApp() {
  const ticks$ = timer(0, 1000);

  return <div>
    <h1>{ ticks$ }</h1>
    <p>seconds passed</p>
  </div>
}
```

<figcaption><a href="https://codesandbox.io/s/recks-example-timer-fjyvj?fontsize=14&hidenavigation=1&theme=dark&module=/src/App" target="_blank">run this example</a> (this one is the same as in the header)</figcaption>

This allows us to define more fine updates with neat syntax.

_Note that the component function will be called only once. When the Observable `timer(0, 1000)` emits a value — the vDOM will be updated in place, without recalculating or updating other parts of the tree_


### State

When we need a local state in a component — we can create one or several [Subjects](https://rxjs.dev/api/index/class/Subject) to write and listen to.

_Subjects are Observables that also let us push values into them. So we can both listen and emit events_

Here's an example:

```jsx
import { Subject } from 'rxjs';
import { map, startWith } from 'rxjs/operators';

function GreetingApp() {
  const name$ = new Subject();
  const view$ = name$.pipe(
    map(x => x ? `Hello, ${x}!` : ''),
    startWith('')
  );

  return <div>
    <input
      placeholder="enter your name"
      onInput={e => name$.next(e.target.value)}
    />
    { view$ }
  </div>
}
```
<figcaption>
<a href="https://codesandbox.io/s/recks-example-greeting-input-tu6tp?fontsize=14&hidenavigation=1&theme=dark&module=/src/App" target="_blank">run the greeting input example</a>
</figcaption>

In the example above when the text field emits an `input` event — we push its value to `name$` stream. `view$` stream that we display derives from `name$` input stream.

_Note that we are using a `startWith` operator for the `view$`: to optimize rendering the engine waits for the first emission from all children before rendering them. So if we remove the `startWith` — `<div>` will be rendered empty, until the `view$` emits a value. Therefore we need to either add a `startWith` operator or to wrap the Observable child with a static child, e.g. `<span>{ view$ }</span>`_


And a more conventional example with a counter:

```jsx
function CounterApp() {
  const input$ = new Subject();
  const view$  = input$.pipe(
      startWith(0),
      scan((acc, curr) => acc + curr)
    );

  return <div>
    <button onClick={ ()=>input$.next(-1) }>minus</button>
    { view$ }
    <button onClick={ ()=>input$.next( 1) }>plus</button>
  </div>
}
```

<figcaption>
<a href="https://codesandbox.io/s/recks-example-counter-lw29e?fontsize=14&hidenavigation=1&theme=dark&module=/src/App" target="_blank">run the counter example</a>
</figcaption>

In this example again we have an `input$` Subject that we'll push updates to. The `view$` Observable accumulates emissions from the `input$` using [scan](https://rxjs.dev/api/operators/scan) operator and will display our state. E.g. when we push `1, 1, 1` to the `input$` — we get a `1, 2, 3` on the `view$`.

### Refs or "real DOM deal"

Sometimes we need to interact with DOM API. For that React [uses](https://reactjs.org/docs/refs-and-the-dom.html) special `ref` objects, that contain a reference to the current DOM element in their `current` property:

```jsx
// A React component
function TextInputWithFocusButton() {
  const inputEl = useRef(null);
  const onButtonClick = () => {
    inputEl.current.focus(); // `current` points to the mounted text input element
  };

  return (
    <div>
      <input ref={inputEl} type="text" />
      <button onClick={onButtonClick}>Focus the input</button>
    <div/>
  );
}
```

Of course in this framework, we get a stream of DOM references! Once a DOM element is created or replaced - the engine pushes a new reference to the stream. We only need to provide the engine with a place for references to be pushed to - a [Subject](https://rxjs.dev/api/index/class/Subject). The engine will push the HTML element to it once it is attached to the real DOM. Thus we get a stream of `HTMLElements` and can apply our logic either to each update or to the latest reference.

Here we'll focus the `<input />` each time the `<button/>` is clicked:


```jsx
// This framework
function TextInputWithFocusButton() {
  const ref$    = new Subject();
  const clicks$ = new Subject();

  clicks$
    .pipe(withLatestFrom(ref$, (_, ref) => ref))
    .subscribe(ref => {
      ref.focus();
    });

  return (
    <div>
      <input ref={ref$} type="text" />
      <button onClick={ ()=>clicks$.next(null) }>Focus the input</button>
    </div>
  );
}
```
<figcaption><a href="https://codesandbox.io/s/recks-example-input-ref-ye5so?fontsize=14&hidenavigation=1&theme=dark&module=/src/App" target="_blank">run the refs example</a></figcaption>


### Subcomponents

So far we had components that only returned Observable results, and didn't have to react to any input. Here's an example of a parent component providing properties to a child component:


```jsx
import { timer } from 'rxjs';
import { map } from 'rxjs/operators';


function Parent () {
  return <div>{
    timer(0, 1000).pipe(
      map(i => <Child index={i} />)
    )
  }</div>
}

function Child (props$) {
  const animal$ = props$.pipe(
    map(props => props.index % 2 ? '🐱' : '🐭')
  )

  return <h1 style="text-align: center;">{animal$}</h1>
}
```
<figcaption>
<a href="https://codesandbox.io/s/recks-example-cat-mouse-hnr41?fontsize=14&hidenavigation=1&theme=dark&module=/src/App" target="_blank">run the cat-mouse example</a>
</figcaption>



When a `Parent` is rendering a `Child` for the first time — it's rendering `<Child index={ 0 } />`. The engine will create a `Child` and push the `{ index: 0 }` props object to the subcomponent's `props$` Observable. The child will immediately react with a mouse 🐭.

Later when the `timer` ticks again and emits `<Child index={ 1 } />` — the engine will only push `{ index: 1 }` to the existing `Child` `props$`.

The `Child` will now produce a cat 🐱.

And so on.

![A cat](https://thepracticaldev.s3.amazonaws.com/i/vlba0ryq9t9v261yqec4.jpg)
<figcaption>An example of a cat that could be produced by a component<br/>Photo by <a href="https://unsplash.com/@michaelsum1228?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Michael Sum</a> on <a href="/s/photos/cat?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></figcaption>

### Redux

For bigger apps, we'll need a bit more sophisticated state management, then just a bunch of Subjects. Any implementation that outputs in an observable way would work with Recks! Let's try [redogs](https://github.com/recksjs/redogs) state manager — it's [redux](https://redux.js.org/), [redux-observable](https://redux-observable.js.org/) and [typesafe-actions](https://github.com/piotrwitek/typesafe-actions) in one small package. Redogs outputs to an Observable, so we'll easily integrate it!

Let's be innovative and create a simple To Do List app as an example 🙂

First, we'll create the store:

```js
import { createStore } from 'redogs';
import { reducer } from './reducer';
import { effects } from './effects';

export const store = createStore(reducer, effects);
```

Now we can access the state changes of the store in our components:

```jsx
import { store } from './store';

function ItemListComponent() {
  const items$ = store.state$.pipe(
    map(state =>
      state.items.map(item => (
        <ItemComponent key={item.id} data={item} />
      ))
    )
  );

  return <div>{items$}</div>;
}
```

Or dispatch events to it:

```jsx
import { store } from './store';

function AddItemComponent() {
  const addItem = event => {
    event.preventDefault();
    const input = event.target['title'];
    store.dispatch(
      addItemAction({
        title: input.value
      })
    );
    input.value = '';
  };

  return (
    <form onSubmit={addItem}>
      <input name="title" type="text" autocomplete="off" />
      <button type="submit">Add</button>
    </form>
  );
}
```

For brevity, I'll skip showing reducers, effects, and other components here. Please, see the full [redux app example at codesandbox](https://codesandbox.io/s/recks-example-redogs-store-2gjvn?fontsize=14&hidenavigation=1&theme=dark&module=/src/App).

_Note that we don't have to learn `reselect` and `re-reselect` APIs to interact with redux._

_We don't have to tweak proprietary `static getDerivedStateFromProps()` or worry about `UNSAFE_componentWillReceiveProps()` and `UNSAFE_componentWillUpdate()` to be efficient with the framework._

_We only need to know Observables, they are lingua franca in Recks._

## Unlike React

For a React component to trigger a self-update — it has to update its state or props (indirectly). React itself will decide when to re-render your component. If you want to prevent unnecessary recalculations and re-renderings — there are several API methods (or hooks), that you can use to _advice_ React how to deal with your component.

In this framework I wanted to make this flow more transparent and adjustable: you directly manipulate the output stream based on the input stream, using well known RxJS operators: [filter](https://rxjs.dev/api/operators/filter), [debounce, throttle, audit, sample](https://thinkrx.io/rxjs/debounceTime-vs-throttleTime-vs-auditTime-vs-sampleTime/), [scan](https://rxjs.dev/api/operators/scan), [buffer](https://thinkrx.io/rxjs/buffer/) and [many](https://rxjs.dev/api)-[many](https://github.com/martinsik/rxjs-extra) others.

**You decide when and how to update your component!**


## Status

Recks source code is published to [github.com/recksjs/recks](https://github.com/recksjs/recks)

To try the framework, you can either:

- run it in [an online sandbox](https://codesandbox.io/s/recks-example-greeting-input-tu6tp?fontsize=14&hidenavigation=1&theme=dark)

- or you can clone a template repository via:

```
git clone --depth=1 https://github.com/recksjs/recks-starter-project.git
cd recks-starter-project
npm i
npm start
```

The package is also available via `npm i recks`, all you need is to set up your JSX transpiler (babel, typescript compiler) to use `Recks.createElement` pragma.

[ Warning ] This is a concept, not a production-ready library.


## Disclaimers

First of all, several times I've called this library a "framework", yet this is no more of a "framework" than react is. So one might prefer to call it "tool" or "library". It's up to you 🙂

Also, my comparisons to React are purely conceptual. React is a mature framework, supported by a smart team of professionals, surrounded by a brilliant community.

This one is a week old, built by me 🐶

## Alternatives

There's one library that provides a React hook to interact with Observables: [rxjs-hooks](https://github.com/LeetCode-OpenSource/rxjs-hooks). It works via a `useState` hook to update the component's state each time an Observable emits, which triggers component re-render. Worth checking out!

Another elephant I should mention here is a real streams-driven framework: [cycle.js](https://cycle.js.org/) by André Staltz. It has a lot of supporters and solid integrations. Cycle.js has a bit different API of using subcomponents and interacting with DOM. Give it a try!

If you know other alternatives — please, share

## Outro

Okay, that's it!

Should this project development continue?
What features would you like to see next?
I'd love to know your thoughts, so leave a comment, please 🙂

If you enjoyed reading this article — press the "heart" and share: this will let me understand the usefulness of this topic and will help others discover this read.

In the following posts, we'll review other Recks integrations, I will share plans for features and publish project updates. So follow me here on [dev.to](https://dev.to/kosich) and [twitter](https://twitter.com/kddsky) to stay tuned!

_I'm proud that you've read so far!_
_Thank you_

## The end


_header photo by [Matthew Smith](https://unsplash.com/@whale?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) on [Unsplash](https://unsplash.com/s/photos/forest?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)_