Immutable Data and Patching
Immutable Data and Patching
Plugin Developer Agent Kit — documentation written for AI agents, readable by humans.
In Jay, ViewState data is immutable. Never mutate objects directly — use signals and JSON Patch for updates.
Immutable Data Model
ViewState objects passed to the render function are immutable snapshots. The framework compares old and new snapshots to determine what changed in the DOM.
// WRONG — never mutate directly
viewState.items.push(newItem);
viewState.count = 5;
// RIGHT — return new values from signals
const [count, setCount] = createSignal(0);
setCount(5);
return { render: () => ({ count: count() }) };
JSON Patch for Complex Updates
For objects with many fields, use createPatchableSignal with JSON Patch operations instead of replacing the entire object:
import { createPatchableSignal } from '@jay-framework/component';
import { REPLACE, ADD, REMOVE } from '@jay-framework/json-patch';
const [state, setState, patchState] = createPatchableSignal({
title: 'Product',
price: 29.99,
tags: ['sale', 'featured'],
details: { color: 'red', size: 'M' },
});
Patch Operations
REPLACE — Update an existing value:
patchState({ op: REPLACE, path: ['price'], value: 19.99 });
patchState({ op: REPLACE, path: ['details', 'color'], value: 'blue' });
ADD — Add a new field or array item:
patchState({ op: ADD, path: ['tags', 1], value: 'new-tag' }); // Insert at index 1
patchState({ op: ADD, path: ['details', 'weight'], value: '500g' });
REMOVE — Remove a field or array item:
patchState({ op: REMOVE, path: ['tags', 0] }); // Remove first tag
patchState({ op: REMOVE, path: ['details', 'size'] });
MOVE — Move a value from one path to another:
import { MOVE } from '@jay-framework/json-patch';
patchState({ op: MOVE, from: ['tags', 0], path: ['tags', 2] }); // Reorder array item
patchState({ op: MOVE, from: ['details', 'color'], path: ['primaryColor'] }); // Relocate field
Multiple Patches
Apply multiple patches at once — the framework batches them into a single update:
patchState(
{ op: REPLACE, path: ['price'], value: 19.99 },
{ op: REPLACE, path: ['details', 'color'], value: 'blue' },
);
When to Use Patch vs Set
- Simple values (number, string, boolean): use
setSignal(newValue) - Objects with few fields: use
setSignal({ ...old, field: newValue }) - Complex nested objects: use
patchStatefor surgical updates - Arrays with identity tracking: use
patchStatewith ADD/REMOVE
createDerivedArray (Map Hook)
Transform an array reactively with smart caching. Only remaps items that actually changed:
import { createDerivedArray } from '@jay-framework/component';
const displayProducts = createDerivedArray(
() => products(),
(item, index, length) => ({
label: `${item().name} - ${formatPrice(item().price)}`,
position: `${index() + 1} of ${length()}`,
}),
);
Key behavior:
- If an item's object identity hasn't changed, the cached mapped result is reused
index()andlength()are tracked — if you don't call them, changes to index/length won't trigger a remap- Returns a
Getter<U[]>— read withdisplayProducts()
See component-state.md for the full hook reference.
About this document
This page is part of the Jay Stack Agent Kit — documentation generated from the framework source and written primarily for AI agents. The language and structure are optimized for machine consumption — expect precise, specification-style prose rather than narrative documentation. Learn more about the Agent Kit →