Computed

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.

When to Use Computed Values∞ 

Use computed values when you need to:

  • Filter or transform lists
  • Combine multiple state values
  • Calculate derived properties
  • Cache expensive calculations
  • Share derived state across components

Basic Computed∞ 

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
  }
}

Performance Benefits∞ 

When you use a computed value:

  1. Automatic dependency tracking: The get function tracks every state path you access
  2. Smart caching: The computed value only recalculates when its dependencies change
  3. Composition optimization: When computed values use other computed values, the dependency tree is maintained

This makes computed values significantly more efficient than calculating derived state in components or actions.

Composition∞ 

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
  }
}

Dynamic Computed with Props∞ 

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.

Creating Computed Factories∞ 

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')
  }
}

Using in Actions∞ 

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
}

Best Practices∞ 

  1. Keep computed values pure: Don’t cause side effects in computed functions
  2. Move complex logic out of components: Extract calculations into computed values
  3. Be specific with dependencies: Only access the state you need
  4. Compose for clarity: Break complex computations into smaller, composable pieces
  5. Watch for expensive operations: Even computed values will run when dependencies change

By following these patterns, computed values can significantly improve your application’s performance and maintainability.