Package @nrgyjs/react
Package Purpose
The @nrgyjs/react package adapts Nrgy.js primitives to React applications. It provides hooks and higher-order components for binding atoms, controllers, and view models to React component trees.
Overview
The package focuses on two integration layers:
- Reactive state binding through
useAtom()anduseAtoms(). - MVC/MVVM composition through
useController(),withViewController(),withViewModel(), andNrgyControllerExtension.
This lets React components consume Nrgy state directly, while controller and view model instances continue to be created and disposed by Nrgy.js rules.
Package Installation
bash
npm install @nrgyjs/core @nrgyjs/react reactbash
yarn add @nrgyjs/core @nrgyjs/react reactbash
pnpm add @nrgyjs/core @nrgyjs/react reactConceptual Architecture
The package is built around @nrgyjs/core declarations and React lifecycle.
useAtom()subscribes a component to a singleAtom<T>.useAtoms()combines an object of atoms into a single computed atom and keeps the resulting object stable with structural equality.useController()creates a controller or view model instance once per declaration, connects it to a React-drivenViewProxy, updates view props, and disposes resources on unmount or declaration replacement.NrgyControllerExtensionstores React-side extension providers in context so nested components can augment controller creation.withViewController()andwithViewModel()wrap React components and inject controller/view-model instances into props.
Functional Documentation
- NrgyControllerExtension: React context for controller extension providers.
- useAtom: Subscribe a component to one atom.
- useAtoms: Read multiple atoms as one object.
- useController: Create and manage controllers or view models in React.
- withViewController: Wrap a component with a controller.
- withViewModel: Wrap a component with a view model.
Usage Examples
tsx
import React from 'react';
import { declareController, readonlyAtom, withView } from '@nrgyjs/core';
import { useAtom, withViewController } from '@nrgyjs/react';
const CounterController = declareController()
.extend(withView<{ initialValue: number }>())
.apply(({ scope, view }) => {
const value = scope.atom(view.props.initialValue());
return {
$value: readonlyAtom(value),
increment: () => value.update((prev) => prev + 1),
};
});
const CounterView = withViewController(CounterController)(
({ controller, initialValue }) => {
const value = useAtom(controller.$value);
return (
<button onClick={controller.increment}>
Start: {initialValue}, current: {value}
</button>
);
},
);tsx
import React from 'react';
import { declareViewModel, readonlyAtom } from '@nrgyjs/core';
import { useAtoms, withViewModel } from '@nrgyjs/react';
const CounterViewModel = declareViewModel(({ scope, view }) => {
const count = scope.atom(view.props.initialValue());
return {
state: { count: readonlyAtom(count) },
increase: () => count.update((prev) => prev + 1),
};
});
const Counter = withViewModel(CounterViewModel)(({ viewModel }) => {
const { count } = useAtoms(viewModel.state);
return <button onClick={viewModel.increase}>{count}</button>;
});