Next.js starter your AI actually understands. Ship internal tools in days not weeks. Pre-order $199 $499 → [Get it now]

How to use selectors in NgRx

NgRx selectors are pure functions that extract and compute derived state from the store with automatic memoization for performance. As the creator of CoreUI with 12 years of Angular development experience, I’ve built complex selector trees in enterprise applications that efficiently compute derived data for millions of users while preventing unnecessary component re-renders.

The most effective approach uses createSelector with memoization for computed state.

Basic Feature Selector

import { createFeatureSelector } from '@ngrx/store'

export interface AppState {
  users: UserState
  products: ProductState
}

export interface UserState {
  users: User[]
  loading: boolean
  selectedId: number | null
}

// Select the feature state
export const selectUserState = createFeatureSelector<UserState>('users')

Simple Property Selectors

import { createSelector } from '@ngrx/store'

// Select specific properties from feature state
export const selectAllUsers = createSelector(
  selectUserState,
  (state: UserState) => state.users
)

export const selectUsersLoading = createSelector(
  selectUserState,
  (state: UserState) => state.loading
)

export const selectSelectedUserId = createSelector(
  selectUserState,
  (state: UserState) => state.selectedId
)

Computed Selectors

// Select active users (computed from all users)
export const selectActiveUsers = createSelector(
  selectAllUsers,
  (users: User[]) => users.filter(user => user.active)
)

// Select user count
export const selectUserCount = createSelector(
  selectAllUsers,
  (users: User[]) => users.length
)

// Select users by role
export const selectAdminUsers = createSelector(
  selectAllUsers,
  (users: User[]) => users.filter(user => user.role === 'admin')
)

Parameterized Selectors

// Select user by ID
export const selectUserById = (userId: number) =>
  createSelector(
    selectAllUsers,
    (users: User[]) => users.find(user => user.id === userId)
  )

// Usage in component
@Component({
  selector: 'app-user-detail',
  template: `<div>{{ user$ | async | json }}</div>`
})
export class UserDetailComponent implements OnInit {
  user$ = this.store.select(selectUserById(this.userId))

  constructor(
    private store: Store,
    private route: ActivatedRoute
  ) {}

  ngOnInit() {
    this.userId = +this.route.snapshot.paramMap.get('id')
  }
}

Combining Multiple Selectors

// Combine selectors from different features
export const selectProductState = createFeatureSelector<ProductState>('products')

export const selectAllProducts = createSelector(
  selectProductState,
  (state: ProductState) => state.products
)

export const selectCartState = createFeatureSelector<CartState>('cart')

export const selectCartItems = createSelector(
  selectCartState,
  (state: CartState) => state.items
)

// Combine data from multiple features
export const selectCartWithProducts = createSelector(
  selectCartItems,
  selectAllProducts,
  (cartItems, products) => {
    return cartItems.map(item => ({
      ...item,
      product: products.find(p => p.id === item.productId)
    }))
  }
)

// Calculate cart total
export const selectCartTotal = createSelector(
  selectCartWithProducts,
  (items) => {
    return items.reduce((total, item) => {
      return total + (item.product?.price || 0) * item.quantity
    }, 0)
  }
)

Selector Composition

// Build complex selectors from simpler ones
export const selectUserViewModel = createSelector(
  selectAllUsers,
  selectUsersLoading,
  selectSelectedUserId,
  (users, loading, selectedId) => ({
    users,
    loading,
    selectedUser: users.find(u => u.id === selectedId),
    totalCount: users.length,
    activeCount: users.filter(u => u.active).length
  })
)

Using Selectors in Components

import { Component, OnInit } from '@angular/core'
import { Store } from '@ngrx/store'
import { Observable } from 'rxjs'
import { selectAllUsers, selectUsersLoading } from './store/user.selectors'

@Component({
  selector: 'app-user-list',
  template: `
    <div *ngIf="loading$ | async">Loading...</div>
    <ul>
      <li *ngFor="let user of users$ | async">
        {{ user.name }}
      </li>
    </ul>
  `
})
export class UserListComponent implements OnInit {
  users$: Observable<User[]>
  loading$: Observable<boolean>

  constructor(private store: Store) {}

  ngOnInit() {
    this.users$ = this.store.select(selectAllUsers)
    this.loading$ = this.store.select(selectUsersLoading)
  }
}

Selector with Props

// Modern approach with props
export const selectUsersByRole = createSelector(
  selectAllUsers,
  (users: User[], props: { role: string }) => {
    return users.filter(user => user.role === props.role)
  }
)

// Usage in component
@Component({
  selector: 'app-role-users',
  template: `<div>{{ users$ | async | json }}</div>`
})
export class RoleUsersComponent {
  @Input() role: string

  users$ = this.store.select(selectUsersByRole, { role: this.role })

  constructor(private store: Store) {}
}

Selector Memoization Benefits

// Selectors are memoized - only recompute when inputs change
export const selectExpensiveComputation = createSelector(
  selectAllUsers,
  (users: User[]) => {
    console.log('Computing expensive result...')

    // This only runs when users array changes
    return users.map(user => ({
      ...user,
      fullName: `${user.firstName} ${user.lastName}`,
      age: calculateAge(user.birthDate)
    }))
  }
)

// Multiple subscriptions share the same memoized result
@Component({
  selector: 'app-dashboard',
  template: `
    <app-user-stats [data]="computed$ | async"></app-user-stats>
    <app-user-chart [data]="computed$ | async"></app-user-chart>
  `
})
export class DashboardComponent {
  // Both subscriptions use the same memoized result
  computed$ = this.store.select(selectExpensiveComputation)

  constructor(private store: Store) {}
}

Selector Testing

import { selectAllUsers, selectActiveUsers } from './user.selectors'

describe('User Selectors', () => {
  const mockState: UserState = {
    users: [
      { id: 1, name: 'John', active: true },
      { id: 2, name: 'Jane', active: false }
    ],
    loading: false,
    selectedId: null
  }

  it('should select all users', () => {
    const result = selectAllUsers.projector(mockState)
    expect(result.length).toBe(2)
  })

  it('should select only active users', () => {
    const allUsers = selectAllUsers.projector(mockState)
    const result = selectActiveUsers.projector(allUsers)
    expect(result.length).toBe(1)
    expect(result[0].name).toBe('John')
  })
})

Best Practice Note

This is how we structure selectors in all CoreUI Angular projects for efficient state management. NgRx selectors provide automatic memoization that prevents unnecessary computations and component re-renders. Always use createSelector for derived state, compose simple selectors into complex ones, and leverage memoization to optimize performance in large applications.

For production applications, consider using CoreUI’s Angular Admin Template which includes pre-configured NgRx with selector patterns.

For complete NgRx implementation, check out how to use NgRx Store in Angular and how to use reducers in NgRx.


Speed up your responsive apps and websites with fully-featured, ready-to-use open-source admin panel templates—free to use and built for efficiency.


About the Author