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
| Method | Description |
|---|---|
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;