Seamless State Management using Async Iterators
Tags: webdev, javascript, node
Published: 2024-06-21 12:55:46

In my recent post about [AI-UI](https://dev.to/matatbread/ive-been-writing-web-backends-and-frontends-since-the-90s-finally-declarative-dynamic-markup-done-right-3jmj), I touched on why I developed the library.

* I wanted declarative markup
* I wanted type-safe component encapsulation, with composition & inheritance
* I wanted a small, fast, client-side solution with no complex build process (or any build process!)

...but most importantly...

* I didn't want to learn a whole new API just to manipulate data _I already have in my app_

In my single page apps, I've got remote APIs and endpoints that supply data. Sure, I might want to do a bit of arithmetic, sorting, formatting, but basically, I want to say to the UI: "Here's my data, render it into the DOM". When the data changes, either because the server tells me or the user manipulates it locally, I don't want to re-render anything - I've already laid out my page - I just want the DOM to automagically re-render.

How can I do this using the most familiar JS syntax possible? **Iterable Properties**

## Show me!

Let's start with one of the basic examples in the AI-UI repo: a [clock](https://raw.githack.com/MatAtBread/AI-UI/main/guide/examples/readme.html).

We're not really interested in the clock itself, we're going to use Chrome Dev tools create iterable properties on a basic JS object, and see how they work. Go to the example, and open Dev Tools with `Ctrl (Cmd) + Shift + I`


![Create an object](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9d38p3d3utc0q05j2z71.png)

We're using this script (as opposed to the ECMA module) as it defines the global constant `AIUI`, so we can just play.

A key sub-module of `AIUI` is the [`Iterators`](https://github.com/MatAtBread/AI-UI/blob/main/guide/iterators.md) interface. Let's grab a reference to it for later use, and just create a plain old JS object.

In the Dev Tools console, type:
```javascript
defineIterableProperty = AIUI.Iterators.defineIterableProperty;
xxx = { foo: 123 };
```
So `xxx` is just an object. Nothing special here. Let's add an iterable property to it. The syntax is `defineIterableProperty(object, key, initialValue)`:

```javascript
defineIterableProperty(xxx,"bar",456);
// xxx.bar just looks like a normal property:
xxx.bar * 10
// 4560
xxx.bar -= 111;
// 345
```

![Add an iterable property and use as normal](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/80ixe8uvavfao3xm23op.png)

But `xxx.bar` has some magic: it's also an async iterator:

```javascript
for await(const v of xxx.bar) console.log("bar is now",v)
// bar is now 345
xxx.bar = 100
// bar is now 100
//100
xxx.bar++
// bar is now 101
// 100
xxx.bar = "Wow!"
// bar is now Wow!
//'Wow!'
```

If you prefer a more functional style, AI-UI comes with a handy bunch of helpers like [`map`, `filter` and `consume`](https://github.com/MatAtBread/AI-UI/blob/main/guide/iterators.md#helper-functions)
```javascript
xxx.bar.map(v => v * 10).consume(v => console.log("Or 10x more is",v));
// Promise {<pending>}  
xxx.bar = 99;
// bar is now 99          <--- for...await is still going!
// Or 10x more is 990     <--- same value mapped
// 99
```

![Watch it change](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v9s0pcnc9526hr86zqvh.png)

## Is that it?
Yep. That's it. No 'setState()', no hooks, signals, or other API to learn. You just define an iterable property, read and write to it like normal, and consume the results as it changes. 

It's this simplicity that makes AI-UI one of the simplest UI modules to use. Just layout your markup specifying iterable values in your variable substitutions like `<div>${xxx.bar}</div>`, and it just works by consuming your iterable values and updating the DOM.

Of course, defining all your own properties by hand is a bit repetitive, so AI-UI components can create a bunch for you using the [`iterable` member](https://github.com/MatAtBread/AI-UI/blob/main/guide/prototype.md#iterable), but that's not the core functionality.

The `Iterators` submodule is pure-JS, with no DOM dependencies, so you can use it in NodeJS apps with

```javascript
import '@matatbread/ai-ui/esm/iterators.js';
```
Start iterating now!