@virentia/react API
Используйте @virentia/react на границе рендеринга. Доменную логику держите в моделях @virentia/core.
ScopeProvider
Передает scope из ядра в React hooks.
Используйте ScopeProvider один раз на React-дерево, которое должно разделять состояние. Вложенные деревья могут предоставить другой scope, если им нужна изоляция.
import { scope } from "@virentia/core";
import { ScopeProvider } from "@virentia/react";
const appScope = scope();
export function App() {
return (
<ScopeProvider scope={appScope}>
<Routes />
</ScopeProvider>
);
}useProvidedScope
Читает scope из ближайшего ScopeProvider.
Используйте этот hook, когда компонент должен передать scope в helpers на границе запуска, кеши или внешние адаптеры.
function SaveButton({ saved }: { saved: EventCallable<void> }) {
const scope = useProvidedScope();
const onClick = () => scoped(scope, () => saved());
return <button onClick={onClick}>Save</button>;
}Если ScopeProvider отсутствует, hook бросит ошибку.
useUnit
Читает сторы и привязывает вызываемые юниты к scope из ScopeProvider.
Используйте useUnit для простых компонентов, которым нужно несколько сторов, событий или эффектов.
const countValue = useUnit(count);
const increment = useUnit(incremented);Объект:
const model = useUnit({
count,
incremented,
pending: saveFx.pending,
save: saveFx,
});Массив:
const [countValue, increment] = useUnit([count, incremented]);useModel
Подготавливает модель для рендера.
Используйте useModel, когда компонент работает с целой моделью и хочет видеть сторы как значения, а события и эффекты как функции для вызова.
const model = useModel({
count,
incremented,
});Создание модели из 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>;
}Используйте кеш, когда модель должна пережить размонтирование компонента:
const model = useModel(createChatModel, props, {
cache: chatCache,
key: props.chatId,
});component
Соединяет фабрику модели и view.
Используйте component, когда модель принадлежит жизненному циклу компонента. Так создание модели, props, события монтирования и размонтирования, а также рендер остаются в одном понятном паттерне.
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>;
},
});Кешированный компонент:
export const ChatPanel = component({
cache: chatCache,
key: (props: { chatId: string }) => props.chatId,
model: createChatModel,
view({ model }) {
return <div>{model.messages.items.length}</div>;
},
});Управляемый компонент:
export const Parent = component({
model() {
const counter = Counter.create({ step: 2 });
return { counter };
},
view({ model }) {
return <Counter step={2} model={model.counter} />;
},
});.create(props) позволяет модели родительского component создать модель дочернего component и владеть ей. Если передать model в дочерний component, этой моделью владеет родитель. Дочерний component обновляет context.props и отправляет lifecycle-юниты пока смонтирован, но не вызывает dispose() для controlled-модели при unmount.
createModelCache
Создает кеш, который учитывает scope и ваш key.
Используйте createModelCache, когда модель должна пережить размонтирование и позже переиспользоваться по key: чаты, вкладки, экраны деталей, медиаплееры, предпросмотры.
const chatCache = createModelCache<string, ChatProps, ChatModel>();Чтение кешированных моделей:
chatCache.has("support", appScope);
chatCache.get("support", appScope);
chatCache.getInstance("support", appScope);Очистка кешированных моделей:
chatCache.delete("support", appScope);
chatCache.clear(appScope);Аргумент scope необязателен у всех методов кеша (has, get, getInstance, delete, clear). Опустите его, чтобы искать ключ по всем scope, которые видел кеш.
ModelContext
Фабрики моделей получают context.
Используйте его, когда логика модели зависит от props, жизненного цикла, текущего scope или ключа кеша.
interface ModelContext<Props, Key = undefined> {
readonly scope: Scope;
readonly owner: Owner;
readonly props: ReactiveWritable<Props>;
readonly mounted: EventCallable<void>;
readonly unmounted: EventCallable<void>;
readonly mounts: StoreWritable<number>;
readonly key: Key;
}mounted, unmounted и mounts полезны для логики жизненного цикла внутри модели.