# Orchestrating State

## Introduction

With ConanJs you should be able to write complex interactions easily. There isn't really a single feature that allows for this, but it really is the combination of many of them.

Some of them we have already seen:

* [Invoking actions](https://docs.conanjs.io/data/actions#invoking-the-actions) directly from the state
* [Chaining actions](https://docs.conanjs.io/data/actions/types-of-actions#chaining-actions). All actions in ConanJs return an [ASAP](https://docs.conanjs.io/asaps), so you can guarantee that you can execute logic immediately after the actions is completed.
* [Composition](https://docs.conanjs.io/data/conan-state/pipes-composing-state). Complex use cases many times requires composing actions like, filter, map...
* [Scoping](https://docs.conanjs.io/data/conan-state/scoping-state). Being able to isolate state to operate on it will also help with complex logic.

But there is a key feature that we have not explored yet, Reactions.

You can add reactions to your ConanState by calling [addDataReaction](https://docs.conanjs.io/data/conan-state). The principle of this very simple, you provide with some logic that will be executed every time the state changes.

## Complex scenario

![Generate alerts based on buy/sell orders and the stock prices](https://3967196510-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3BBQqsBUGjmzTvukhE%2F-MBipd2Gg9vxEjAatvdS%2F-MBirIq5m9CJNqTpxVjZ%2Forchestration.gif?alt=media\&token=a98ea9e0-b910-4520-9d9f-6c06601b5fea)

### Initial atomic state

These example starts with four atomic states.

```typescript
const stock$ = Conan.light<StockPrice[]>('stock', [{
    id: 'TSLA',
    price: 1000
},{
    id: 'AAPL',
    price: 350
}]);

const stockOrder$ = Conan.light<StockOrder[]>('stockOrders', [{
    stockId: 'AAPL',
    buy: 300,
    sell: 400
},{
    stockId: 'TSLA',
    buy: 900,
    sell: 1200
}]);

const alertsByStock$ = Conan.light<IKeyValuePairs<StockAlert[]>>('alerts', {});

const alertStream$ = alertsByStock$.map<StockAlert[]>(alertsByStock => {
    let newStream: StockAlert[] = [];
    Objects.foreachEntry(alertsByStock, (stockAlerts)=>newStream = [...newStream, ...stockAlerts])
    return newStream.sort((left, right)=>left.timestamp - right.timestamp);
});
```

**stock$** has the code and price for each stock

**stockOrder$** has the buy and sell orders that will trigger alarms

**alertsByStock$** the list of alerts by stock key based on stock$ and stockOrder$

**alertStream$** as we want to show a list of alerts, we use this derived state to build the list of states based on alertsByStock$ (this is explained further below)

### Generating the alerts

Alerts are generated based on any change in either the **stock$** or the **stockOrder$,** below you can see the logic for this.

```typescript
stockOrder$.tuple(stock$).addDataReaction({
    name: `checking alerts`,
    dataConsumer: ([stockOrders, stocks]) => {
        let newAlerts: StockAlert[] = [];
        stockOrders.forEach(stockOrder => {
            const stock = stocks.find(it => it.id === stockOrder.stockId);
            let operation: 'buy' | 'sell' | 'keep';
            if (stock.price >= stockOrder.sell) {
                operation = 'sell';
            } else if (stock.price <= stockOrder.buy) {
                operation = 'buy';
            } else {
                operation = 'keep';
            }
            newAlerts.push({
                operation,
                orderSnapshot: stockOrder,
                stockSnapshot: stock,
                timestamp: Date.now()
            })
        });

        let nextState: IKeyValuePairs<StockAlert[]> = {...alertsByStock$.getData()};
        newAlerts.forEach(newAlert => {
            if (nextState[newAlert.stockSnapshot.id] == null) {
                nextState[newAlert.stockSnapshot.id] = [newAlert];
            } else {
                let alertsForStock: StockAlert[] = nextState[newAlert.stockSnapshot.id];
                let lastAlert = alertsForStock[alertsForStock.length - 1];
                if (
                    !Objects.deepEquals(newAlert.stockSnapshot, lastAlert.stockSnapshot) ||
                    !Objects.deepEquals(newAlert.orderSnapshot, lastAlert.orderSnapshot)
                ) {
                    alertsForStock.push(newAlert);
                }
            }
        })

        alertsByStock$.do.update(nextState);
    }
})
```

#### �Merging two states with a tuple:

At the top we [merge with a tuple](https://docs.conanjs.io/data/pipes-composing-state#tuple) the stock orders, and the stock, this generate a new ConanState that will contain an array of two elements, the stock orders and the stock prices, if any of them changes, a new state will be created.

```typescript
[...] stockOrder$.tuple(stock$) [...]
```

#### Adding a reaction:

Immediately after, we add a reaction to the tuple

```typescript
[...] .addDataReaction({
    name: `checking alerts`,
    dataConsumer: ([stockOrders, stocks])=> {
    [....]
    }
})
```

#### Invoking an action:

Note that the bulk of the logic is to decide if based on the new price and order information a new alert to keep / buy or sell should be generated.

The alerts are built on the back of the reaction into:

```typescript
let nextState: IKeyValuePairs<StockAlert[]>
```

The alerts are a map of stock code to any the list of alerts they have.

Once this object is built, all we need to do is to update the alerts state.

```
alertsByStock$.do.update(nextState);
```

### Streaming the alerts

As mentioned at the beginning we ultimately want to see a stream of alerts on the screen.

Note how we easily manage to do this by composing a new state via map from **alertsByStock$**

```typescript
const alertStream$ = alertsByStock$.map<StockAlert[]>(alertsByStock => {
    let newStream: StockAlert[] = [];
    Objects.foreachEntry(alertsByStock, (stockAlerts)=>newStream = [...newStream, ...stockAlerts])
    return newStream.sort((left, right)=>left.timestamp - right.timestamp);
});
```

![](https://3967196510-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3BBQqsBUGjmzTvukhE%2F-MBimtIaV7Yyndolton8%2F-MBioncopFrP0Vne6d48%2Fmagic.gif?alt=media\&token=a06564bd-b52a-469d-8d20-1fbdbefb86f0)

## See the code by yourself

You can see this in action here:

{% embed url="<https://codesandbox.io/s/github/conan-js/conan-js-examples/tree/master/orchestratingState>" %}
