Core Concepts
React integration
Bind rs-x expressions to React components with useRsxExpression and useRsxModel — components re-render automatically when model values change.
Practical value
Key points
- Zero boilerplate — pass a model and an expression string, the hook returns a live value.
- useRsxExpression accepts either a string (creates its own expression, owns the lifecycle) or a pre-built IExpression (subscribes only, does not dispose on unmount).
- useRsxModel recursively binds every scalar field of an object, returning a mirrored object whose fields are live reactive values.
- Collections (arrays, maps, sets) are not supported by useRsxModel — they break React's hooks ordering rules. Use useRsxExpression with a leafWatchRule instead.
- getExpressionFactory() and getExpressionManager() are singleton helpers that auto-initialise the rs-x DI container on first call — no manual setup required.
useRsxExpression — string expression example
Pass a model and an expression string. The component re-renders whenever firstName or lastName changes on the model.
import { useRsxExpression } from '@rs-x/react';
const model = {
firstName: 'Jane',
lastName: 'Doe',
};
function FullName() {
// Re-renders automatically whenever firstName or lastName changes
const fullName = useRsxExpression<string>('firstName + " " + lastName', { model });
return <span>{fullName}</span>;
}useRsxExpression — pre-built IExpression example
Build the expression once outside the component and share it. The hook subscribes to changes but does not dispose the expression on unmount.
import { rsx } from '@rs-x/expression-parser';
import { useRsxExpression } from '@rs-x/react';
// Create a shared expression once — outside the component
const model = { price: 100, quantity: 3 };
const totalExpr = rsx<number>('price * quantity')(model);
function OrderTotal() {
// Pass the pre-built IExpression — no model needed
const total = useRsxExpression(totalExpr);
return <span>Total: {total}</span>;
}
// Mutate the model anywhere — OrderTotal re-renders automatically
model.quantity = 5;useRsxExpression — leafWatchRule example
Narrow which array indices trigger a re-render by passing a leafWatchRule. Useful for large collections where you only care about specific items.
import { useRsxExpression } from '@rs-x/react';
import type { IIndexWatchRule } from '@rs-x/state-manager';
const model = {
items: ['apple', 'banana', 'cherry'],
};
// Watch only index 0 — the component ignores changes to other indices
const watchFirstOnly: IIndexWatchRule = { indices: [0] };
function FirstItem() {
const first = useRsxExpression<string>('items', {
model,
leafWatchRule: watchFirstOnly,
});
return <span>First item: {first?.[0]}</span>;
}useRsxModel — full model binding example
Bind every scalar field in a model object. Each field is independently reactive — React only re-renders the subtree that depends on what changed.
import { useRsxModel } from '@rs-x/react';
const model = {
user: {
name: 'Alice',
age: 30,
},
score: 95,
};
function UserCard() {
// Recursively binds every scalar field — each field re-renders independently
const { user, score } = useRsxModel<typeof model, typeof model>(model);
return (
<div>
<p>{user.name} — age {user.age}</p>
<p>Score: {score}</p>
</div>
);
}
useRsxModel — field filter example
Pass an optional FieldFilter predicate to exclude fields from binding. Useful for internal or non-reactive properties.
import { useRsxModel, type FieldFilter } from '@rs-x/react';
const model = {
name: 'Alice',
_internal: 'skip this',
score: 95,
};
// Only bind fields that don't start with an underscore
const publicFieldsOnly: FieldFilter = (_parent, field) => !field.startsWith('_');
function UserCard() {
const { name, score } = useRsxModel<typeof model, { name: string; score: number }>(
model,
publicFieldsOnly,
);
return (
<p>{name} — {score}</p>
);
}getExpressionFactory example
Access the underlying IExpressionFactory singleton directly. Useful outside React components — for example in service files or utility hooks.
import { getExpressionFactory } from '@rs-x/react';
const model = { x: 10, y: 20 };
// Get the singleton factory — auto-initialises the rs-x module on first call
const factory = getExpressionFactory();
const sum = factory.create<number>(model, 'x + y');
sum.changed.subscribe(() => {
console.log('sum:', sum.value); // logs whenever x or y changes
});
model.x = 42; // → logs "sum: 62"getExpressionManager example
Access the IExpressionManager singleton to parse expressions without binding them to a model.
import { getExpressionManager } from '@rs-x/react';
const model = { a: 1, b: 2 };
const manager = getExpressionManager();
// Parse an expression without binding it to a model
const parsed = manager.parse('a + b');
console.log(parsed); // expression ASTInstallation example
Install all required packages.
npm install @rs-x/core @rs-x/state-manager @rs-x/expression-parser @rs-x/reactRelated docs
- Angular integrationRsxPipe and providexRsx() for Angular templates
- Async operationsMix Promise/Observable/expression values with sync values
- Member expressionsNested property and member access
- Modular expressionsCompose reusable expression parts
- CollectionsArray/Map/Set guide with specific-item monitoring examples