Сторы
Стор хранит значение модели. Если приложению нужно что-то помнить между событиями, это почти всегда стор: текст поля, выбранный id, список сообщений, статус формы, кешированные данные.
const query = store("");
const profile = store({ name: "Ada", age: 36 });Стор не является глобальной переменной. Он описывает, какое значение существует в модели, а конкретное значение лежит в scope. Поэтому один и тот же стор может иметь разные значения в разных scopes.
const first = scope();
const second = scope();
scoped(first, () => {
query.value = "docs";
});
scoped(second, () => {
query.value = "api";
});В первом scope у query будет "docs", во втором — "api". Код модели при этом один и тот же.
Чтение и запись
Сторы с примитивными значениями читаются через .value.
count.value += 1;Стор, хранящий объект, всё равно читается через .value. Чтобы обновить его, присвойте новое значение:
profile.value = { ...profile.value, age: profile.value.age + 1 };Для объектного состояния, которое нужно читать и писать по полям, используйте reactive (см. раздел «Реактивные сторы» ниже).
Глубоко вложенное состояние
Для состояния, которое вы правите на месте, вглубь — документы, модели редакторов, большие таблицы с правкой по ячейкам — см. @virentia/mutable. Его .value — глубоко мутабельный draft (doc.value.blocks[3].items[7].text = "…") с той же реактивностью по keypath: производное значение перезапустится только для прочитанных им частей.
Прямое чтение или запись требуют scope в текущем контексте выполнения. Если код не находится внутри реакции, effect handler или другой ветки, где scope уже известен, откройте его через scoped(scope, fn).
Записи в сторы транзакционные. Несколько записей в одном синхронном дереве вызовов юнитов батчатся в один commit, а реакции видят финальное закоммиченное значение.
reaction({
on: incremented,
run() {
count.value++;
count.value++;
},
});Внутри транзакции следующий код читает текущее draft-значение. Снаружи транзакции подписчики и derived-сторы уведомляются после commit.
Полная модель исполнения описана в разделе Транзакции.
Реактивные сторы
reactive — это стор, поля объекта которого читаются и пишутся напрямую на самом юните, без обёртки .value. Используйте его, когда состояние — это объект, и обновления по полям читаются естественнее, чем замена значения целиком.
const form = reactive({ name: "Ada", age: 36 });
form.age += 1;
form.name = "Grace";reactive подчиняется тем же правилам, что и store: значение живёт в scope, прямые чтение и запись требуют активного scope, а записи транзакционные.
Используйте readonlyReactive для объекта, который модель показывает наружу, но меняет только через свои реакции или эффекты. Потребители могут читать поля, но не могут им присваивать.
const profile = readonlyReactive({ name: "Ada", age: 36 });Оба принимают необязательный skip-токен и devtools-опции, как store.
Derived-сторы
Derived-стор нужен, когда значение полностью следует из другого значения. Например, label поиска можно вычислить из query, а не обновлять вручную в каждой реакции.
const queryLabel = query.map((text) => (text ? `Searching: ${text}` : "Search"));Derived-сторы ленивые. Если на такой стор не подписана реакция или UI, изменение source-стора только помечает его кеш грязным. Пересчет произойдет позже, когда значение явно прочитают. Если derived-стор активен, например на него подписана реакция, изменение зависимостей сразу пересчитает значение и запустит подписчиков только при реальном изменении результата.
Если значение зависит от события во времени, это уже не derived-стор. Тогда лучше хранить обычный стор и менять его через реакцию.
Ленивые вычисления
computed нужен для значений, которые выводятся из состояния, но не должны пересчитываться до чтения. Это полезно для тяжелой фильтрации, сортировки, сборки view-model или правил, где зависимости удобнее определить по фактическим чтениям.
const query = store("");
const users = store({ items: [] as User[] });
const visibleUsers = computed(() => {
const text = query.value.toLowerCase();
return users.value.items.filter((user) => user.name.toLowerCase().includes(text));
});visibleUsers ведет себя как read-only стор. Первый read в scope выполнит функцию и запомнит результат. Следующие чтения вернут кеш. Когда query или users изменятся в этом же scope, кеш станет грязным, но значение не пересчитается, пока его снова не прочитают или пока его не наблюдает реакция, подписка или UI.
Обычный store() считается активным всегда: запись должна сохранить новое значение. Derived-сторы активируются только наблюдением. Используйте map, когда нужен простой derived-стор от одного источника. Используйте computed, когда вычисление дорогое, зависит от нескольких сторов или зависит от веток внутри самой функции.