withViewModel.tsx
Purpose
This module provides the withViewModel() HOC, which creates a view model and passes it to a React component through the viewModel prop.
Overview
withViewModel() targets MVVM scenarios where a visual component expects a viewModel object and typically reads state through useAtoms(). The module also exports withViewModelImpl(), which serves as the internal implementation and can be reused by extensions.
Conceptual Architecture
The module wraps useController() with a type-safe layer for ViewModelDeclaration.
withViewModel()accepts a view-model declaration and returns an HOC factory.withViewModelImpl()builds the wrapper component, separateschildrenfrom the remaining props, and passes the rest intouseController().- The resulting component renders the original
ViewComponent, forwardingchildren, the original props, and the createdviewModel.
This design preserves strict typing through InferViewModelProps and removes the need for repetitive manual casting in React views.
Public API Description
withViewModel<TViewModel>(viewModelDeclaration)
viewModelDeclaration: view-model declaration compatible withViewControllerContext<InferViewModelProps<TViewModel>>.- Returns a function that accepts a component with a
viewModelprop and creates an HOC with automatically inferred view-model props.
withViewModelImpl(viewModelDeclaration, ViewComponent)
- Internal helper for constructing the HOC.
- Accepts a view-model declaration and a React component.
- Returns a component that creates the
viewModelthroughuseController().
Usage Examples
tsx
import React from 'react';
import { declareViewModel, readonlyAtom } from '@nrgyjs/core';
import { useAtoms, withViewModel } from '@nrgyjs/react';
const CounterViewModel = declareViewModel(({ scope, view }) => {
const value = scope.atom(view.props.initialValue());
return {
state: { value: readonlyAtom(value) },
increase: () => value.update((prev) => prev + 1),
};
});
const Counter = withViewModel(CounterViewModel)(({ viewModel }) => {
const { value } = useAtoms(viewModel.state);
return <button onClick={viewModel.increase}>{value}</button>;
});