# A Step-by-Step Walkthrough

Since the traditional `melody-component` API can cause a lot of boilerplate code and requires the developer to become familiar with several technologies (Redux, higher-order components, possibly redux-observable), we were looking for an API that was simpler, allowed for less code when writing components, and was still as powerful.

The result is melody-streams.

### What does it look like?

![](https://1622223910-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LRjc7qp5YL3fbxJv0XO%2F-Lj8KhALsD7lGINP4k05%2F-Lj84bFWPUllZ4eDT6Fu%2Fmelody-counter.jpg?alt=media\&token=29d413b9-e575-4c9f-8770-cab3492cee51)

Let us look at a simple counter component with internal state (the current counter value), a "plus one" button, and a "minus one" button. The implementation with `melody-streams` might look like this:

{% code title="index.twig" %}

```markup
<div class="counter">
  <h3>Counter: {{ count }}</h3>
  <button type="button" ref="{{ incrementButtonRef }}"> + </button>
  <button type="button" ref="{{ decrementButtonRef }}"> - </button>
</div>
```

{% endcode %}

{% code title="index.js" %}

```javascript
import { createComponent, attachEvent } from "melody-streams";
import template from "./index.twig";
import { merge } from "rxjs";
import { mapTo, scan, startWith } from "rxjs/operators";

const Counter = () => {
    const [incrementButtonRef, incrementClicks] = attachEvent("click");
    const [decrementButtonRef, decrementClicks] = attachEvent("click");

    const count = merge(
        incrementClicks.pipe(mapTo(1)),
        decrementClicks.pipe(mapTo(-1))
    ).pipe(
        scan((acc, curr) => acc + curr),
        startWith(0)
    );
    return {
        count,
        incrementButtonRef,
        decrementButtonRef
    };
};

export default createComponent(Counter, template);
```

{% endcode %}

### Step by step

What is going on here? Let's examine it one step at a time.&#x20;

First, we import the `createComponent` function from `melody-streams`. This function expects a component function and a template as arguments. We will call our component function `Counter`. By convention, component functions are capitalised. Moreover, we import a template file (`index.twig`).

{% code title="index.js" %}

```javascript
import { createComponent } from "melody-streams";
import template from "./index.twig";

const Counter = () => {};

export default createComponent(Counter, template);
```

{% endcode %}

As you can see, the component function and the template are both passed to `createComponent`.&#x20;

But what does this component function have to do?

First and foremost, it should return a stream of `state` objects, i.e., an [Observable](http://reactivex.io/rxjs/class/es6/Observable.js~Observable.html). Whenever a new (and different from the previous) state is delivered by this stream, Melody will re-draw the component. The contents of the state object will be available in the template.&#x20;

Our template looks like this:

{% code title="index.twig" %}

```markup
<div class="counter">
  <h3>Counter: {{ count }}</h3>
  <button type="button" ref="{{ incrementButtonRef }}"> + </button>
  <button type="button" ref="{{ decrementButtonRef }}"> - </button>
</div>
```

{% endcode %}

Therefore, we need to provide state objects that contain the fields `count`,  as well as `incrementButtonRef` and `decrementButtonRef`:

{% code title="index.js" %}

```javascript
import { createComponent } from "melody-streams";
import template from "./index.twig";

const Counter = () => {
    return {
        count: ...,
        incrementButtonRef: ...,
        decrementButtonRef: ...
    };
};

export default createComponent(Counter, template);
```

{% endcode %}

Unfortunately, we do not have these values yet. Creating them is easy, however. Let's start with `attachEvent`, which gets us already half way there:

{% code title="index.js" %}

```javascript
import { createComponent, attachEvent } from "melody-streams";
import template from "./index.twig";

const Counter = () => {
    const [ref1, clicks1] = attachEvent("click");
    const [ref2, clicks2] = attachEvent("click");
    
    return {
        count: ...,
        incrementButtonRef: ref1,
        decrementButtonRef: ref2
    };
};

export default createComponent(Counter, template);
```

{% endcode %}

As you can see, `attachEvent` takes only an event name as parameters (in fact, it can take multiple event names, but that does not matter here). Therefore, the result is not bound to any particular DOM element *yet*. More on that in a second.&#x20;

`attachEvent` returns an array with two values:

* A reference handler (which we name `ref1`)
* An Observable of the requested events (in this case, click events)

By adding the reference handlers to the object returned from the component function (`Counter`), we make them available to the template. Here, we map `ref1` to `incrementButtonRef`, and `ref2` to `decrementButtonRef`. This is when the results of `attachEvent` become bound to actual DOM elements.&#x20;

We still have to create the `count` value. This value will depend on how many times the "plus one" and the "minus one" buttons have been clicked. We get this information from the Observables `clicks1` and `clicks2`, respectively. Moreover, we want to start with zero.

We assemble the functionality we need by using Rx.js operators and constructors.

{% code title="index.js" %}

```javascript
import { merge } from "rxjs";
import { mapTo, scan, startWith } from "rxjs/operators";

const counts = merge(
    clicks1.pipe(mapTo(1)),
    clicks2.pipe(mapTo(-1))
).pipe(
    scan((acc, curr) => acc + curr, 0),
    startWith(0)
);
```

{% endcode %}

Putting it all together, with a little bit of variable renaming, the result looks like this:

{% code title="index.js" %}

```javascript
import { createComponent, attachEvent } from "melody-streams";
import template from "./index.twig";
import { merge } from "rxjs";
import { mapTo, scan, startWith } from "rxjs/operators";

const Counter = () => {
    const [incrementButtonRef, incrementClicks] = attachEvent("click");
    const [decrementButtonRef, decrementClicks] = attachEvent("click");

    const count = merge(
        incrementClicks.pipe(mapTo(1)),
        decrementClicks.pipe(mapTo(-1))
    ).pipe(
        scan((acc, curr) => acc + curr),
        startWith(0)
    );
    
    return {
        count,
        incrementButtonRef,
        decrementButtonRef
    };
};

export default createComponent(Counter, template);
```

{% endcode %}

Here, we used the same variable names as are used in the template, so we can use the shortened object notation for the return value of `Counter`.

This simple component covers only some of what the `melody-streams` API can do. Of course, you want to receive props, you want to send events, and do other things. This is covered in the following sections.
