createState
Creates a reactive state container with getter and setter methods. Provides a simple state management solution with observable state.
Example
Section titled “Example”const [state, setState, { updateState, resetState }] = createState(0);
const stateHistory: number[] = [];
state.subscribe((value: number) => { stateHistory.push(value);});
assert.deepStrictEqual(stateHistory, [0]);
setState(10); // logs: 10
assert.deepStrictEqual(stateHistory, [0, 10]);
updateState((prev: number) => prev + 1); // logs: 11
assert.deepStrictEqual(stateHistory, [0, 10, 11]);
resetState(); // logs: 0
assert.deepStrictEqual(stateHistory, [0, 10, 11, 0]);Skipping equal updates (equals)
Section titled “Skipping equal updates (equals)”The second argument takes an equals option.
const [filters, setFilters] = createState( { label: 'bug', page: 1 }, { equals: fastDeepEqual },);
const before = filters.getSnapshot().value;
// Derived state is recomputed only when the state really changes.const query = filters.pipe( map(({ label, page }) => `label:${label} page:${page}`),);
const queryHistory: string[] = [];
query.subscribe((value: string) => { queryHistory.push(value);});
setFilters({ label: 'bug', page: 1 }); // equal: nothing is passed on
assert.strictEqual(filters.getSnapshot().value, before);
setFilters({ label: 'bug', page: 2 });
assert.deepStrictEqual(queryHistory, ['label:bug page:1', 'label:bug page:2']);equals is called with the current and the next state. When it returns true, the update is not passed on: no subscriber, and nothing derived from the state, is told of it, and the current state is kept along with its identity (setState, updateState and resetState return the current state rather than the next). Decide this where the state is held rather than downstream with skipIfNoChange: an update passed on here reaches whatever reads the state directly before any operator can thin it out.
equals defaults to Object.is, so writing the value already held passes nothing on. Pass equals: () => false to pass on every update, equal or not.
Keeping the parts that did not change (keepEqualParts)
Section titled “Keeping the parts that did not change (keepEqualParts)”A state written whole from a source that answers with a new object every time, such as a server polled for a report, can take keepEqualParts: true.
type PullReport = DeepReadonly<{ summary: { open: number }; pulls: { number: number; title: string }[];}>;
const [report, setReport] = createState<PullReport>( { summary: { open: 0 }, pulls: [] }, { keepEqualParts: true },);
const summary = report.pipe(map((r) => r.summary));
const summaryHistory: unknown[] = [];
summary.subscribe((value) => { summaryHistory.push(value);});
// A server answers each poll with the whole report, a new object every// time. What did not change keeps the object already held.setReport({ summary: { open: 1 }, pulls: [{ number: 1, title: 'one' }],});
const held = report.getSnapshot().value;
// An answer that changes nothing is not passed on at all.setReport({ summary: { open: 1 }, pulls: [{ number: 1, title: 'one' }],});
assert.strictEqual(report.getSnapshot().value, held);
// An edited title: a new report and a new array, while the summary is the// one held, so `summary` is not told of it.setReport({ summary: { open: 1 }, pulls: [{ number: 1, title: 'one (edited)' }],});
assert.notStrictEqual(report.getSnapshot().value, held);
assert.strictEqual(report.getSnapshot().value.summary, held.summary);
assert.deepStrictEqual(summaryHistory, [{ open: 0 }, { open: 1 }]);The next state is compared with the current one before it is written: every part deeply equal to the same part of the current state is replaced by that part, so whatever reads an unchanged part keeps the object it has and map, comparing with Object.is, stops there, and an update that changes nothing is not passed on at all. Array items are matched by index; keepEqualParts: { key } matches them by key. It is the writing-side counterpart of the keepEqualParts operator, and the one to prefer.