Computed Values
Overview
Computed values in neutrix let you create derived state that automatically updates when its dependencies change. Think of them like spreadsheet formulas—they recalc automatically when any dependency changes.
Basic Usage
1. Creating a Computed Value
You can define a computed value on your store by calling store.computed(path, fn)
, where:
path
: a unique key identifying the computed resultfn
: a function that calculates the derived data from your state
Here's a simple example:
import { createNeutrixStore } from 'neutrix';
const { store, useStore } = createNeutrixStore({
todos: [
{ id: 1, text: 'Learn Neutrix', completed: false },
{ id: 2, text: 'Write docs', completed: true }
]
});
const activeTodos = store.computed(
'todos.active',
state => state.todos.filter(todo => !todo.completed)
);
function TodoList() {
useStore(s => s.todos);
const active = activeTodos();
return <div>Active Todos: {active.length}</div>;
}
store.computed('todos.active', ...)
:
Creates a named computed value and caches it for reuse across components.
It returns a function that you call, e.g.
activeTodos()
.Automatic Recalculation: Whenever the underlying
state.todos
changes, callingactiveTodos()
will return the updated result.Component Re-render: Your component will re-render if you subscribe to the relevant parts of state, e.g.
useStore(s => s.todos)
.
2. Using useNeutrixComputed for Component-Specific Computed Values
Sometimes, you want a one-off or component-specific computed value that you don’t need to store globally or name. In that case, useNeutrixComputed(fn)
calculates a derived result on-demand and automatically tracks dependencies.
import React from 'react';
import { useNeutrixComputed } from 'neutrix';
import { store, useStore } from './store';
function TodoStats() {
const activeTodos = useNeutrixComputed(state => {
return state.todos.filter(todo => !todo.completed);
});
const total = useStore(s => s.todos.length);
return (
<div>
<p>Total Todos: {total}</p>
<p>Active Todos: {activeTodos.length}</p>
</div>
);
}
Differences vs. store.computed:
store.computed(path, fn)
- Named and cached in the store.
- Multiple components can call that function (e.g.,
activeTodos()
).
useNeutrixComputed(fn)
- Only lives in the current component.
- Perfect for ephemeral or single-use derived data.
3. Automatic Dependency Tracking
Neutrix uses a proxy-based system to automatically track dependencies:
const userStatus = useNeutrixComputed(store => {
if (!store.user.isLoggedIn) return 'Offline';
if (store.user.lastActive < Date.now() - 5000) return 'Away';
return 'Online';
});
No manual dependency arrays—Neutrix knows when store.user.isLoggedIn
or store.user.lastActive
changes and re-computes accordingly.
Performance Features
1. LRU Caching
Computed values are cached using an LRU (Least Recently Used) cache:
- Default cache size: 50 entries
- Automatic cache invalidation when dependencies change
- Memory-efficient for large applications
2. Dependency Graph
const fullName = useNeutrixComputed(store => {
// dependency graph tracks these automatically:
const first = store.user.firstName; // Dependency 1
const last = store.user.lastName; // Dependency 2
return `${first} ${last}`;
});
When either user.firstName
or user.lastName
changes, fullName
is re-evaluated.
3. Circular Dependencies
Neutrix automatically detects and prevents circular dependencies:
const circular = store.computed('circular', state => {
return circular(); // Error: Circular dependency detected
});
React Integration
1. Using with Hooks
function UserProfile() {
const fullName = useNeutrixComputed(store => {
return `${store.user.firstName} ${store.user.lastName}`;
});
const userStatus = useStore(store => store.computed.userStatus());
return (
<div>
<h2>{fullName}</h2>
<p>Status: {userStatus}</p>
</div>
);
}