Skip to content
manolo edge edited this page Jun 3, 2025 · 8 revisions

State

The state offers a way to create reactive values that can be used with tags to build dynamic and responsive web apps. This documentation will guide you through the usage of the state and listState function and its features.

Table of Contents

state

When you create a state , it creates a Observable. Observables can then used across Cardboard for different things, take a look at the Observables guide to get a better understanding on how they work.

utilities

There are a couple of utilities that can help with manipulating list states.

stateAdd

Add item to array Observable.

const myList = state([]);
stateAdd(myList, 4); // Adds 4 to myList

stateAddAt

Add item at index in array Observable.

const myList = state([]);
stateAddAr(myList, 4, 2); // Adds 4 to myList at index 2

stateRemove

Remove item from array Observable.

const myList = state([1]);
stateRemove(myList, 1); // Removes item 1 from myList

stateRemoveWhere

Remove item from array Observable.

const myList = state([1]);
stateRemove(myList, (item) => item == 1); // Removes item 1 from myList

Examples

Here are some examples of how to use the state function and work with reactive state:

Single Value State

The simplest way of using the state is to make individual value states:

import { state } from 'cardboard-js/dist/cardboard.js';

const count = state(0);

// Listen to changes in the entire state
count.changed((count) => {
  console.log('Count changed:', count);
});

// Modify the state and trigger change events
count.value++; // Increment the count
count.value = 3; // Set the count to 3
count.dispatch(3); // Set the count to 3

Object State

You can create states with any value you like. For examples you can create a state with an object instead of a primitive.

import { state } from 'cardboard-js/dist/cardboard.js';

const { value, changed } = state({ count: 0 });
changed(({ count }) => { /* something in the object changed */ });

value.count++;

div('There are no items').hideIf(value.count); // Will be hidden when the list contains items

Using With Arrays

As mentioned above, you can also create array states:

const st = state([]);
st.value = [...st.value, 'new item'];

const hasItems = notEmpty(st);

div('There are no items').hideIf(hasItems); // Will be hidden when the list contains items

notEmpty is a built-in compute helper, this means it returns a new Observable that updates it's value when the state st changes. It will tell you if the list is empty or not.
Take a look at "Computing" for a more detailed explanation.

You can also use each to render lists dinamically.

const colors = ['red', 'orange', 'yellow', 'green', 'blue', 'indigo', 'violet'];

div(
    each(colors, (color) => 
        button(color).addStyle('color', color)
    )
);

Conditional Rendering

You can conditionally/reactively change properties and do certain actions with tags. Like hiding an element, adding classes, etc... Take a look at Conditional Element Manipulation for more detailed explanations.

import { state } from 'cardboard-js/dist/cardboard.js';

const isLoading = state(true);

// Hide an element if isLoading is true
div().hideIf(isLoading);

// Disable an element if isLoading is true
div().disableIf(isLoading);

// Add class "loading" when isLoading is true, and add class "idle" when isLoading is false
div()
  .classIf(isLoading, ['loading'])
  .classIfNot(isLoading, ['idle'])

isLoading.value = false;

Template Rendering

You can also use state to reactively update text. Take a look at Managing Text for more detailed explanations.

import { state } from 'cardboard-js/dist/cardboard.js';

const st = state({ count: 0 });

// Create a div with a text that displays the count
div(text('Count is: $count', st));
div().text('Count is: $count', st);
div('Count is: ', st.count);

listState

For handling lists you can create a listState, which gives some extra utilities, like adding/removing items, getting the count (as a consumable), etc... It also makes items into Observables, this way you can interact with items in more complex ways.

List state returns a custom object instead of a Observable. It contains a list with the data, the length (as a Observable), and methods to modify the data. You can then use the list as explained here, and you can also add and remove items from the list.

Example

import { listState } from './state.js';

const numbers = listState([1, 2]);
div(
  each(numbers.list, (n) => p(`item ${n}`))
);

numbers.add(3);
numbers.remove(2);
numbers.addAt(4, 3);

Any time the numbers change, each will update the content accordingly.

Properties

Once you have created a reactive list using listState, you can use the following methods and properties to manage and access the list:

  1. add(item: T): Adds an item to the end of the list.
  • item (T): The item to add to the list.
  1. addAt(item: T, index: number): Adds an item at a specific index in the list.
  • item (T): The item to add to the list.
  • index (number): The index at which to insert the item.
  1. remove(item: T): Removes an item from the list based on the value.
  • item (T): The item to remove from the list.
  1. removeWhere(cb: (item: T) => boolean): Removes items from the list based on a provided condition.
  • cb (Function): A callback function that returns true for items to be removed and false for items to be retained.
  1. list (Property): The underlying Observable. You can use this property to access the list directly.
  2. listValue (Property): A property that returns the current values of the reactive list as a regular array.
  3. length (Property): A Observable representing the current length of the list.
Clone this wiki locally