Computed values let you derive state from your application state tree. Instead of putting calculation logic in components or actions, computed values give you a dedicated place for this logic with automatic optimization.
Use computed values when you need to:
Let’s look at a simple example that filters a list based on criteria in state:
import { state } from 'cerebral'
export const filteredList = (get) => {
const items = get(state`items`)
const filter = get(state`filter`)
return items.filter((item) => item[filter.property] === filter.value)
}
Add it to your state tree:
import { filteredList } from './computed'
export default {
state: {
items: [
{ id: 1, title: 'Item 1', isCompleted: false },
{ id: 2, title: 'Item 2', isCompleted: true }
],
filter: {
property: 'isCompleted',
value: false
},
filteredList
}
}
When you use a computed value:
get
function tracks every state path you accessThis makes computed values significantly more efficient than calculating derived state in components or actions.
You can compose computed values by using other computed values:
import { state } from 'cerebral'
// First computed
export const activeUsers = (get) => {
const users = get(state`users`)
return users.filter((user) => user.isActive)
}
// Second computed using the first
export const activeAdmins = (get) => {
const active = get(state`activeUsers`)
return active.filter((user) => user.isAdmin)
}
In your state tree:
import { activeUsers, activeAdmins } from './computed'
export default {
state: {
users: [
/* user objects */
],
activeUsers,
activeAdmins
}
}
Computed values can access component props, making them dynamic:
import { state, props } from 'cerebral'
export const userItems = (get) => {
const userId = get(props`userId`)
const items = get(state`items`)
return items.filter((item) => item.userId === userId)
}
Use it in a component:
// React example
connect(
{
items: state`userItems`
},
function UserItems({ items }) {
return (
<ul>
{items.map(item => (
<li key={item.id}>{item.title}</li>
))}
</ul>
)
}
)
// When using the component
<UserItems userId="user-123" />
Behind the scenes, Cerebral clones the computed for each component instance to maintain individual caching.
Sometimes you’ll want to create reusable computed patterns. You can create a computed factory:
import { state } from 'cerebral'
export const itemsByStatus = (statusKey) => (get) => {
const items = get(state`items`)
const status = get(state`${statusKey}`)
return items.filter((item) => item.status === status)
}
// Usage in state
export default {
state: {
items: [],
activeStatus: 'pending',
archivedStatus: 'archived',
// Created from factory
pendingItems: itemsByStatus('activeStatus'),
archivedItems: itemsByStatus('archivedStatus')
}
}
You can use computed values in actions just like any other state:
function myAction({ get, store }) {
const filteredItems = get(state`filteredList`)
if (filteredItems.length === 0) {
store.set(state`noResults`, true)
}
}
For computed values that require props, pass them as the second argument:
function myAction({ get }) {
const userItems = get(state`userItems`, { userId: 'user-123' })
// Do something with userItems
}
By following these patterns, computed values can significantly improve your application’s performance and maintainability.