HYDROXIDE

State Utilities

Perform complex state updates on nested objects and arrays

When working with nested objects, you often need to update a value deep inside the structure. Hydroxide provides a simple path-based syntax for this.

The Problem

In most frameworks, you can't mutate state directly. Instead, you must create a new object with the updated value. This is also true for Hydroxide - You can't mutate the state directly. Instead, you must use the set() or do() methods to update the state.

For nested objects, this means manually copying every level of the structure:

const newValue = {
  ...oldValue,
  foo: {
    ...oldValue.foo,
    bar: {
      ...oldValue.foo.bar,
      bazz: oldValue.foo.bar.bazz + 1
    }
  }
}

This gets verbose quickly. For deeply nested data, you end up with spreading at every level. This is error-prone, hard to read, and obscures what you're actually trying to do: change one value.

Hydroxide's state utilities solve this by letting you target nested values directly.

Path-Based Updates

Hydroxide lets you target any nested value by calling the reactive with a path:

import { reactive } from 'hydroxide'

const state = reactive({
  foo: {
    bar: {
      bazz: 0
    }
  }
})

// Update bazz directly
state('foo', 'bar', 'bazz').set(10)

The path ('foo', 'bar', 'bazz') navigates to state.foo.bar.bazz. Then you can use any update method like set() or do().

Using set() and do()

Both update methods work with paths:

// Replace with a new value
state('foo', 'bar', 'bazz').set(100)

// Transform the current value
state('foo', 'bar', 'bazz').do(v => v + 1)
import { reactive } from 'hydroxide';

function Example() {
  const state = reactive({
    foo: {
      bar: {
        bazz: 0
      }
    }
  });

  function increment() {
    state('foo', 'bar', 'bazz').do(v => v + 1);
  }

  return (
    <button on-click={increment} class="primary-button">
      count is {state().foo.bar.bazz}
    </button>
  );
}

export default Example;

Array Methods

Hydroxide provides utility methods for performing array operations which internally.

Using these methods also gives more hints to the Hydroxide runtime about what you're trying to do, which allows it to optimize the DOM updates even more

MethodDescription
push(item)Add item to end
pop()Remove last item
insert(index, item)Insert item at index
remove(index, count?)Remove item(s) at index
swap(a, b)Swap items at indices a and b
pushList(items)Add multiple items to end
insertList(index, items)Insert multiple items at index
clear()Remove all items
const tasks = reactive(['A', 'B', 'C'])

tasks.push('D') // ['A', 'B', 'C', 'D']
tasks.pop() // ['A', 'B', 'C']
tasks.insert(0, 'X') // ['X', 'A', 'B', 'C']
tasks.remove(0) // ['A', 'B', 'C']
tasks.swap(0, 2) // ['C', 'B', 'A']
import { reactive } from "hydroxide";
import { List } from "hydroxide-dom";

function TodoApp() {
  const input = reactive("");
  const todos = reactive([
    { task: "Learn Hydroxide", done: false },
    { task: "Build something", done: true },
    { task: "Ship it", done: false },
  ]);

  function toggleDone(index) {
    todos(index, "done").do((done) => !done);
  }

  function removeTodo(index) {
    todos.remove(index);
  }

  function addNewTask() {
    if (input() === "") return;
    todos.push({ task: input(), done: false });
    input.set("");
  }

  function handleKeyDown(e) {
    if (e.key === "Enter") {
      addNewTask();
    }
  }

  return (
    <div class="app">
      <div class="input-container">
        <input
          type="text"
          bind-value={input}
          on-keydown={handleKeyDown}
          placeholder="Create Task"
        />
        <button class="primary-button" on-click={addNewTask}>
          Add
        </button>
      </div>
      <ul>
        <List.Indexed
          each={todos()}
          as={(todo, index) => (
            <li class={todo().done ? "done" : ""}>
              <span class="task">{todo().task}</span>
              <button class="toggle" on-click={() => toggleDone(index())}>
                {todo().done ? "✓" : "○"}
              </button>
              <button class="remove" on-click={() => removeTodo(index())}>
                X
              </button>
            </li>
          )}
        />
      </ul>
    </div>
  );
}

export default TodoApp;

On this page