Skip to content

Calculated Fields

Calculated fields are derived values computed from the current state. They’re added to the state object automatically and available in both the view and reducers.

Each calculated field is a function that receives the current state and returns the derived value:

UserProfile.calculated = {
fullName: (state) => `${state.firstName} ${state.lastName}`,
isAdult: (state) => state.age >= 18,
itemCount: (state) => state.items.length
}
function UserProfile({ state }) {
return (
<div>
<h1>{state.fullName}</h1>
<p>{state.isAdult ? 'Adult' : 'Minor'}</p>
<p>Items: {state.itemCount}</p>
</div>
)
}

By default, calculated fields recalculate on every state change. To skip unnecessary recalculations, declare dependencies using the tuple form [depsArray, fn]:

OrderSummary.calculated = {
// Only recalculates when state.items changes
subtotal: [['items'], (state) => state.items.reduce((sum, i) => sum + i.price, 0)],
// Only recalculates when subtotal changes
tax: [['subtotal'], (state) => state.subtotal * 0.08],
// Only recalculates when subtotal or tax changes
total: [['subtotal', 'tax'], (state) => state.subtotal + state.tax],
// No deps — always recalculates (original behavior)
label: (state) => `${state.items.length} items`,
}

Dependencies can reference both base state keys and other calculated field names. When a dependency names another calculated field, that field is guaranteed to be computed first.

An empty deps array [[], fn] means the field computes once and never recalculates — useful for constant derived values.

Calculated Fields Depending on Other Calculated Fields

Section titled “Calculated Fields Depending on Other Calculated Fields”

Calculated fields can depend on other calculated fields. The framework automatically determines the correct computation order using a topological sort at component creation time:

Component.calculated = {
doubled: [['value'], (state) => state.value * 2],
quadrupled: [['doubled'], (state) => state.doubled * 2],
octupled: [['quadrupled'], (state) => state.quadrupled * 2],
}

Circular dependencies are detected at component creation time and throw an error:

// This throws: "Circular calculated dependency: a → b → a"
Component.calculated = {
a: [['b'], (state) => state.b + 1],
b: [['a'], (state) => state.a + 1],
}

By default, calculated fields are stored in the actual state. To prevent this:

UserProfile.storeCalculatedInState = false

When set to false, calculated fields are still available in the view and reducers but don’t persist in the state tree. This is useful when calculated values are expensive or transient.

If a calculated field has the same name as a key in initialState, a warning is logged at component creation time. The calculated field will always overwrite the base state value, so the initialState entry is effectively dead.