Skip to content

Reactivity & State Management

Brandon Jordan edited this page Oct 13, 2023 · 3 revisions

State management is built into jsUI to make a single element reactive to a store() using the .model() method on elements or by using the Model() component in your view to make multiple elements reactive.

Some reactivity comes from the element event methods, but this adds reactivity based on data.

Data Stores

Stores are kind of like variables in that they keep track of a single value, however, they can be attached to element(s) so they can model the value and react when it updates. To update the value of the store, reference it and use the .set() or .update() methods.

Create a store:

import {store} from 'javascript-ui';

// Create a store with an initial value of 0.
const counter = store(0);

Update a store's value:

// Set a new value.
counter.set(1);

// Set a new value based on the current value.
counter.update((value) => {
    return ++value;
});

Get a store's current value:

counter.get();

Do something when the value of a store updates:

// Run a function every time the store is updated.
counter.model((newValue) => {
    // do something...
});

Reactive element methods

.model(store, ?callback)

The model() method can be chained to any element to model the inner content of the element after the provided store. This can be handled automatically or manually by providing an optional callback, which will be provided the element and the new store value.

// ...
Element()
    .model(store, (element, newValue) {
        // ...
    })
// ...

.if(store)

This will conditionally hide or show the element based on the evaluated value of a provided store.

// ...
Element()
    .if(store)
// ...

Model component

The Model() component takes a store and a callback that is expected to return a single element or component or an array of elements or components. These elements will be re-rendered when the store value is updated.

// ...
Model(store, (value) => {
    return [
        // ...
    ];
})
// ...

Store files

Here is an example of a "store file" using the store object.

We export the store for use in model methods or updating the store value and we also provide a default export for rendering the modeled data by default. This also reacts to the store being updated in the same file and sends a request to update the value in the database.

import {store, Text} from 'javascript-ui';

export const counter = store(0);

window.onload = () => {
    fetch('/counter/get').then(response => {
        counter.set(response);
        counter.model(newValue => {
            if (newValue !== counter.get()) {
                fetch('/counter/update', {
                    count: newValue,
                }).then(response => {
                    if (response.statusCode !== 200) {
                        throw new Error('Unable to save value');
                    }
                });
            }
        });
    });
};

export default [
    Text().model(counter),
];

We can then reference this in two ways.

Using it's default export

This will render text of the store value or whatever you want to export by default for this store.

import counter from './stores/counter.js';

view([
    counter,
    // ...
]);

Or reference the store for a model:

import {counter} from './stores/counter.js';

view([
    Text().model(counter),
    // ...
]);