Component bindings
While the state allows to interact with it imperatively, inside components you probably want to utilize built-in methods to automatically track value changes.
state.track
state.track(cb)state.track(cb, options?: { callOnMount, skipFirstCall, comparator })
Subscribes to all state updates, automatically unsubscribe when the component where it is called unmounts.
By default:
- the callback runs immediately with the current value
- future calls run when the value changes by referential equality (
===)
You can use:
callOnMount: trueto defer the first call until the component is mountedskipFirstCall: trueto skip the initial callcomparatorto control when updates should be considered equal
state.track((value) => {
console.log("new value", value);
});
state.trackSelected
state.trackSelected(selector, cb)state.trackSelected(selector, cb, options?: { callOnMount, skipFirstCall, comparator })
Subscribes to a selected part of the state and only triggers when that selected value changes.
user$.trackSelected(
(user) => user.name,
(name) => {
console.log("new name", name);
},
);
state.render
state.render()state.render(value => Veles.Node, comparator?)
render renders markup based on the current state value and updates it whenever the value changes.
const title$ = createState("hello");
return title$.render((title) => <p>{title}</p>);
If called without a callback, it renders the value directly.
const title$ = createState("hello");
return title$.render();
state.renderSelected
state.renderSelected(selector)state.renderSelected(selector, selectedValue => Veles.Node, comparator?)
Works like render, but first selects a smaller piece of the state. This will make updates more atomic.
const task$ = createState({ title: "task", completed: false });
return task$.renderSelected((task) => task.title, (title) => <p>{title}</p>);
This can be used for conditionals.
title$.renderSelected(
(title) => title.length > 100,
(isTooLong) => (isTooLong ? <Warning /> : null),
);
state.attribute
state.attribute()state.attribute(value => attributeValue)
attribute is used for reactive DOM attributes. When the value changes, only that specific DOM Node’s attribute will be changed.
const disabled$ = createState(false);
return <button disabled={disabled$.attribute()} />;
You can also transform the value first.
const width$ = createState(100);
return <div style={width$.attribute((value) => `width: ${value}px`)} />;
state.renderEach
state.renderEach({ key: "id" }, ({ elementState: element$ }) => ...)state.renderEach({ key, selector }, ({ elementState: element$, indexState: index$ }) => ...)
renderEach is the optimized way to render arrays. It works by comparing old and new states and only making necessary DOM changes, e.g. inserting a new component into a specific position, or simply swapping 2 nodes without re-rendering anything. It wraps each individual value into the state object, which allows to avoid any unnecessary re-renders.
const tasks$ = createState([
{ id: "1", title: "first" },
{ id: "2", title: "second" },
]);
return tasks$.renderEach({ key: "id" }, ({ elementState: element$ }) => {
return <div>{element$.renderSelected((task) => task.title)}</div>;
});
The callback receives:
element$: state for the current itemindex$: state for the current item index