@virentia/react API
Use @virentia/react at the rendering boundary. Keep domain logic in @virentia/core models.
ScopeProvider
Provides a core scope to hooks.
Use it once per React tree that should share state. Nested trees can provide another scope when they need isolation.
import { scope } from "@virentia/core";
import { ScopeProvider } from "@virentia/react";
const appScope = scope();
export function App() {
return (
<ScopeProvider scope={appScope}>
<Routes />
</ScopeProvider>
);
}useProvidedScope
Reads the provided scope.
Use it when a component needs to pass the scope into boundary helpers such as allSettled, caches, or external adapters.
function SaveButton({ saved }: { saved: EventCallable<void> }) {
const scope = useProvidedScope();
const onClick = () => allSettled(saved, { scope });
return <button onClick={onClick}>Save</button>;
}It throws when there is no ScopeProvider.
useUnit
Reads stores and binds callable units to the provided scope.
Use it for simple components that only need a few stores, events, or effects.
const countValue = useUnit(count);
const increment = useUnit(incremented);Object shape:
const model = useUnit({
count,
incremented,
pending: saveFx.pending,
save: saveFx,
});Array shape:
const [countValue, increment] = useUnit([count, incremented]);useModel
Unwraps a model object.
Use it when a component works with a whole model and wants stores as values and events/effects as callbacks.
const model = useModel({
count,
incremented,
});Creates a model from props:
function createCounterModel({ props }: ModelContext<{ step: number }>) {
const clicked = event<void>();
const count = store(0);
reaction({
on: clicked,
run() {
count.value += props.step;
},
});
return { clicked, count };
}
function Counter(props: { step: number }) {
const model = useModel(createCounterModel, props);
const increment = () => model.clicked();
return <button onClick={increment}>{model.count}</button>;
}Use a cache when the model should survive unmount:
const model = useModel(createChatModel, props, {
cache: chatCache,
key: props.chatId,
});component
Pairs a model factory and a view.
Use it when the model belongs to the component lifecycle. It keeps creation, props, mount events, unmount events, and rendering in one pattern.
export const Counter = component({
model({ props }: ModelContext<{ step: number }>) {
const clicked = event<void>();
const count = store(0);
reaction({
on: clicked,
run() {
count.value += props.step;
},
});
return { clicked, count };
},
view({ model }) {
const increment = () => model.clicked();
return <button onClick={increment}>{model.count}</button>;
},
});Cached component:
export const ChatPanel = component({
cache: chatCache,
key: (props: { chatId: string }) => props.chatId,
model: createChatModel,
view({ model }) {
return <div>{model.messages.items.length}</div>;
},
});Controlled component:
export const Parent = component({
model() {
const counter = Counter.create({ step: 2 });
return { counter };
},
view({ model }) {
return <Counter step={2} model={model.counter} />;
},
});.create(props) lets a parent component model create and own a child component model. Passing model to the child keeps that child model controlled by the parent. The child component updates context.props and emits lifecycle units while mounted, but it does not dispose a controlled model on unmount.
createModelCache
Creates a scope-aware cache keyed by your ID.
Use it when a model should survive unmount and be reused later by key: chats, tabs, detail screens, media players, previews.
const chatCache = createModelCache<string, ChatProps, ChatModel>();Read cached models:
chatCache.has("support", appScope);
chatCache.get("support", appScope);
chatCache.getInstance("support", appScope);Dispose cached models:
chatCache.delete("support", appScope);
chatCache.clear(appScope);ModelContext
Model factories receive:
Use this context when model logic depends on props, lifecycle, the current scope, or a cache key.
interface ModelContext<Props, Key = undefined> {
readonly scope: Scope;
readonly owner: Owner;
readonly props: StoreWritable<Props>;
readonly mounted: EventCallable<void>;
readonly unmounted: EventCallable<void>;
readonly mounts: StoreWritable<number>;
readonly key: Key;
}Use mounted, unmounted, and mounts for lifecycle logic inside the model.