# 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](/data/conan-state/actions.md#invoking-the-actions) directly from the state
* [Chaining actions](/data/conan-state/actions/types-of-actions.md#chaining-actions). All actions in ConanJs return an [ASAP](/asaps.md), so you can guarantee that you can execute logic immediately after the actions is completed.
* [Composition](/data/conan-state/pipes-composing-state.md). Complex use cases many times requires composing actions like, filter, map...
* [Scoping](/data/conan-state/scoping-state.md). 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](/data/conan-state.md). 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](/files/-MBirIq5m9CJNqTpxVjZ)

### 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](/data/conan-state/pipes-composing-state.md#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);
});
```

![](/files/-MBioncopFrP0Vne6d48)

## 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>" %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.conanjs.io/data/conan-state/orchestrating-state.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
