Совместимость с Effector
@virentia/effector нужен, когда модель Virentia должна работать рядом с уже существующим кодом на Effector.
Существующий код Effector продолжает импортировать effector, а модели Virentia используют @virentia/core. Этот пакет связывает их scope и позволяет одному пограничному юниту участвовать в обоих runtime.
Установка
pnpm add @virentia/effector effector @virentia/coreAssociation scope
Создайте одну association для scope Virentia и scope Effector, которые относятся к одному рендеру, тесту, request или границе приложения:
import { scope } from "@virentia/core";
import { associate } from "@virentia/effector";
import { fork } from "effector";
const virentiaScope = scope();
const effectorScope = fork();
const association = associate({
virentia: virentiaScope,
effector: effectorScope,
});Association хранятся глобально в weak map. Объекта совместимости больше нет, dispose() тоже нет. Association достижима, пока достижимы ее scope.
Нужны оба scope. Если fooled-юнит запускается без association, пакет бросит ошибку, а не создаст скрытый scope.
Универсальные юниты
Используйте fool(unit) на границах фич. Возвращенное значение — проходной юнит, который можно использовать и в Effector, и в Virentia:
import { event } from "@virentia/core";
import { fool } from "@virentia/effector";
export const checkoutRequested = fool(event<{ orderId: string }>());Фичи на Effector могут использовать этот юнит как clock, source или target. Фичи на Virentia могут слушать его через reaction/on и вызывать внутри run или scoped.
Прямые вызовы
fool(original) возвращает новый универсальный юнит и не мутирует original. Сохраняйте и передавайте возвращенное значение. Вызов original остается вызовом исходного runtime-юнита, а не fooled-обертки. Мост может увидеть этот исходный вызов и передать его через связанные scope, но гибридный API есть только у значения, которое вернул fool.
Virentia в Effector
Фича на Virentia может владеть командой, а фича на Effector может потреблять эту команду как Effector clock:
import { event, scoped } from "@virentia/core";
import { fool } from "@virentia/effector";
import { createEvent, createStore, sample } from "effector";
const checkoutRequested = fool(event<{ orderId: string }>());
function createVirentiaCheckoutFeature() {
return {
requestCheckout: checkoutRequested,
};
}
function createEffectorBillingFeature() {
const $session = createStore({ token: "session-token" });
const billingStarted = createEvent<{ orderId: string; token: string }>();
sample({
clock: checkoutRequested,
source: $session,
fn: (session, request) => ({
orderId: request.orderId,
token: session.token,
}),
target: billingStarted,
});
return {
billingStarted,
};
}
const checkout = createVirentiaCheckoutFeature();
const billing = createEffectorBillingFeature();
await scoped(virentiaScope, () => checkout.requestCheckout({ orderId: "order:1" }));Вызов Virentia идет в virentiaScope. Мост берет association и запускает Effector clock в связанном effectorScope.
Effector в Virentia
Фича на Effector может владеть командой, а фича на Virentia может слушать тот же пограничный юнит:
import { event, reaction } from "@virentia/core";
import { fool } from "@virentia/effector";
import { allSettled, createEvent, sample } from "effector";
const routeOpened = fool(createEvent<string>());
function createEffectorRoutesFeature() {
const profileClicked = createEvent<string>();
sample({
clock: profileClicked,
target: routeOpened,
});
return {
profileClicked,
};
}
function createVirentiaAnalyticsFeature() {
const profileTracked = event<{ route: string }>();
reaction({
on: routeOpened,
run(route) {
profileTracked({ route });
},
});
return {
profileTracked,
};
}
const routes = createEffectorRoutesFeature();
const analytics = createVirentiaAnalyticsFeature();
await allSettled(routes.profileClicked, {
scope: effectorScope,
params: "/users/1",
});Effector-граф запускается в effectorScope. Мост берет association и запускает Virentia reaction в связанном virentiaScope.
Поиск scope
Когда fooled-юнит запускается внутри Effector-графа, мост читает stack.scope и находит связанный scope Virentia. Когда fooled-юнит запускается внутри scoped, мост читает текущий scope Virentia и находит связанный scope Effector.
Используйте scoped, Effector allSettled, scopeBind, launch или UI Providers, чтобы выбрать scope. Мост только переводит запуск между уже связанными scope.