Write Angular like a pro. Angular Icon

Follow the ultimate Angular roadmap.

NGRX Store: Understanding State Selectors

Selectors are pure functions that take slices of state as arguments and return some state data that we can pass to our components. To better understand what selectors are and what they do, it helps see ngrx state as a data structure - a tree that can be serialised to JSON. Data is added to the state tree by composing state in reducers - that’s the easy part. Now to get data out of the state tree, we have to traverse it to find our property of interest - and return it. That can become more complex, and is where selectors help us out.

You may have already seen the store.select method being used to get data from the store by passing it a string value:

this.store.select('pizzas');

The string represents the name of a slice of state in the store and we can expect this function to return data corresponding to our pizzas property - perhaps an array of pizzas. However, store.select can also take a function instead, which takes a slice of state and returns a property from the state (which you’ve likely already seen as well):

this.store.select((state) => state.pizzas);

Both of these approaches represent the concept of a selector - we are “selecting” state!

So, when we think of ngrx/store as a database and the selectors are like SELECT in SQL queries - they gives us back information we want. As our state tree grows deeper, it would become more complex to get state out of the store.

We may find ourselves writing complex data transformation logic in our components to get exactly what we need - however we don’t want to do that - and this is where selectors come into play. Just like with databases, we can perform data transformation from within the store by composing selectors to just return what we need. We’ll keep our components lean and decoupled from the Store.

Thinking about data structures

Let’s first visualise this concept of state as a data structure outside of the NGRX context. We create a JavaScript class Store that has a state property and some initial values:

class Store {
  constructor() {
    this.state = {
      products: {
        pizzas: {
          entities: {
            1: { name: 'Pizza 1', id: 1 },
            2: { name: 'Pizza 2', id: 2 },
          },
        },
      },
    };
  }
}

Let’s pay close attention to the structure of state. The state object is just a regular JavaScript object that has properties defined and embedded within. One object property wraps another object property and so on, creating a hierarchy or “tree” with state acting as the root. Traversing the complete state tree looks like this, if we wanted to fetch our entities:

state
  -> products
    -> pizzas
      -> entities

In order to get to a particular property, we have to traverse the tree. For example, we build our way to entities like building a chain and each level down is a link that connects us from state to entities. If we miss any link in the chain, it breaks and we cannot create the connection. Each link in this chain represents a reference to that state property. Hence, we need a reference to products, then a reference to pizzas, and finally a reference to entities. Once there, we can access the data held by entities.

What do we mean by “reference to a property”? To illustrate this concept, we are going to create an instance of the Store class and showcase different ways in which we can access the properties of the state object:

const store = new Store();

Now, store is yet another JavaScript object containing a state property in it. So one of the ways that we can access the levels of the property chain is through the familiar dot notation. Let’s grab our entities using this approach for now:

const entities = store.state.products.pizzas.entities;

This method is surely easy, but we’ll find ourselves typing this chain over and over whenever we need to get to a desired property. This isn’t the most efficient approach for reusable logic, and it’s also error prone with deep property references - if something was undefined it blows up.

Angular Directives In-Depth eBook Cover

Free eBook

Directives, simple right? Wrong! On the outside they look simple, but even skilled Angular devs haven’t grasped every concept in this eBook.

  • Green Tick Icon Observables and Async Pipe
  • Green Tick Icon Identity Checking and Performance
  • Green Tick Icon Web Components <ng-template> syntax
  • Green Tick Icon <ng-container> and Observable Composition
  • Green Tick Icon Advanced Rendering Patterns
  • Green Tick Icon Setters and Getters for Styles and Class Bindings

So, what if we could create shortcuts to every link in the chain? We could create functions that return products, pizzas and entities independently:

const getProducts = (state) => state.products;
const getPizzas = (state) => state.pizzas;
const getEntities = (state) => state.entities;

Notice how convenient these functions are. Taking getEntities as example, the purpose of the function is to pass it some state and from that state extract and return the entities property. It looks as if we are having direct access to the entities property or direct access to that level. We could refer to this function as a “state shortcut”, but I’d like to call it a state selector function instead.

What’s missing here is how to pass state to the getEntities selector without using store.state directly - otherwise, we’ll be dependent again on dot notation. The solution? We’ll add a select method to our Store class that passes the state object to our upcoming selector function:

class Store {
  // ...
  select(fn) {
    return fn(this.state);
  }
}

Our select method takes a callback function and invokes it whilst passing state as the argument. Using this method to get entities, we could pass state throughout selectors in a logical way with each passing getting us down a level of the state tree till we hit entities:

const getProducts = (state) => state.products;
const getPizzas = (state) => state.pizzas;
const getEntities = (state) => state.entities;

const entities$ = store.select((state) => {
  const products = getProducts(state);
  const pizzas = getPizzas(products);
  const entities = getEntities(pizzas);
  return entities;
});

As we have shown before, first we get products. Once we have products we can get pizzas and from it we get entities. This method is nice and easy and certainly works, but we can take this one step further and make it declarative and functional by using function composition to create a single callback that gets passed to select:

const getProducts = (state) => state.products;
const getPizzas = (state) => state.pizzas;
const getEntities = (state) => state.entities;

const entities$ = store.select((state) =>
  getEntities(getPizzas(getProducts(state)))
);

Function composition is when you get a single result back by embedding functions in each other: the return value of the inner function becomes the argument of the outermost function and so on. Here, we are composing our selectors to give us back the value of entities.

We’ve seen that a selector function is a pure function that grants us direct access to the value of a state tree traversal. We use selectors to avoid manually traversing the state tree over and over, and in return, we get powerful declarative functional programming for our state management. Now that the concept of selectors is clear, let’s take a look at why understanding it is important to grasp selectors in NGRX. Let’s move on and see what this same data structure would look like with NGRX.

Feature state selectors

Our Store in NGRX is initialised with a root state - the top level in our state tree. As we keep our application nice and modular, we are going to create additional entries in the state tree. We keep our Angular apps modular by using feature modules, and NGRX has support for this too! Once our lazily-loaded Angular module is instantiated - it adds itself to our root Angular app - and the same applies with NGRX Store (and Effects too!). This means once we lazy load an Angular module which also manages state, it’s automatically bound to our root state as well.

Adding this behaviour is nice and easy - we just register any feature state into feature modules by importing the StoreModule and invoking it with .forFeature():

StoreModule.forFeature('products', reducers);

The first argument of .forFeature contains a string that represents the name of the feature state, and the second argument supplies our reducers which manage that feature state. The feature name plays a crucial role in creating a state selector for the feature state using a handy function provided by ngrx/store called createFeatureSelector.

createFeatureSelector allows us to get a top-level feature state property of the state tree simply by calling it out by its feature name:

export const getProductsState = createFeatureSelector('products');

So what’s happening here with createFeatureSelector? Firstly, we pass it a string that represents the name used to register the feature state in the feature module. It uses this string to look up the feature state from within the root state object, such as state['products'].

It then returns a typed selector function that will return a reference to that specific slice of state.

So, createFeatureSelector returns a selector function that looks up and returns the specified feature state. The generic type passed to it is the type of feature state that we get from the selector function. In this case, the selector will return a feature state of type ProductState. Our ProductState will then be managed by various reducers, we’ll take a look at it momentarily.

Now that we have easy access to the products state slice through getProductsState, we can use it in our component as follows:

this.store
  .select(fromStore.getProductsState)
  .map((state) => state.pizzas)
  .map((pizzas) => pizza.entities);

To get the state we need, we have to rely on mapping via .map() to extract it from the top-level feature state. We are dot walking ProductState with each map call. This works nicely, but again it’s repetitive, not reusable and more difficult to test as a unit. This is where createSelector comes into play, and we’ll work out how we can combine it with our new createFeatureSelector.

State slice selectors

Due to being pure functions that return a slice of state, selector functions can be composed together to be consumed by components, they may be composed from various pieces of our overall state - and this is where state management becomes more important as we need to get things correct from the get-go.

To kickstart composition, we need to define a starting point - our top-level feature. We are able to easily get a reference to a top-level state property by using createFeatureSelector. Once we have that reference, we can then compose it with other selectors that point to state further down in our feature state - effectively traversing the state tree till we get to a desired property. We did something similar in one of the examples of a previous section using pure functions. Let’s see how we can do the same within our store.

We start at the point where state is defined and managed: the reducer. We’re going to use the app from my comprehensive NGRX course:

// src/products/store/reducers/index.ts
import { ActionReducerMap, createFeatureSelector } from '@ngrx/store';

import * as fromPizzas from './pizzas.reducer';
import * as fromToppings from './toppings.reducer';

export interface ProductsState {
  pizzas: fromPizzas.PizzaState;
  toppings: fromToppings.ToppingsState;
}

export const reducers: ActionReducerMap<ProductsState> = {
  pizzas: fromPizzas.reducer,
  toppings: fromToppings.reducer,
};

export const getProductsState =
  createFeatureSelector<ProductsState>('products');

ProductsState represents the feature state of this feature module. It is composed of two other states that reside one level below in the state tree: PizzaState and ToppingsState. Our ProductsState is managed by our reducers (an ActionReducerMap that contains two reducers - pizzas and toppings) and each one manages the individual lower-level states, respectively. Let’s visualise what the state tree would look like now purely as a JavaScript object:

// RootState
state = {
  // ProductState
  products: {
    // PizzaState
    pizzas: {
      entities: {},
      loaded: false,
      loading: true,
    },
    // ToppingsState
    toppings: {
      entities: {},
      loaded: false,
      loading: true,
    },
  },
};

To get to our pizza entities, we’d need to follow this path like we saw in the beginning:

state -> products -> pizzas -> entities

Now we can introduce createSelector to obtain references to properties further down our state tree - which allows us to ask for pizzas whenever we need in a simple way.

We’ve already defined getProductsState as a feature selector that gives us back the state slice that corresponds to ProductsState. All that’s left is to compose it with other selectors to start building a chain down our state tree. This feels like we’re setting up a lot of boilerplate at times, and we are in places, but once it’s setup - we’re ready to use it thousands of times with little adjustments - selectors are fantastic to use and scale nicely with huge datasets and multiple states.

So, let’s dive that one level deeper and use createSelector to jump down another level:

// src/products/store/reducers/index.ts
export interface ProductsState {
  pizzas: fromPizzas.PizzaState;
  toppings: fromToppings.ToppingsState;
}

export const getProductsState =
  createFeatureSelector<ProductsState>('products');

export const getPizzaState = createSelector(
  getProductsState,
  (state: ProductsState) => state.pizzas
);

Note how we pass getProductsState as the first argument - so we can begin our state lookup from this point. And just like that, we have access to a property located further down in our state tree.

The createSelector function takes up to eight selector functions as arguments, each one referencing different slices of state. The last argumet to createSelector can be treated as our “projector function”. Let’s take a look at one of the TypeScript definitions for createSelector to further grasp this before continuing:

export function createSelector<State, S1, S2, S3, Result>(
  s1: Selector<State, S1>,
  s2: Selector<State, S2>,
  s3: Selector<State, S3>,
  projector: (s1: S1, s2: S2, s3: S3) => Result
): MemoizedSelector<State, Result>;

We don’t need to panic ourselves with all the typings here - but let’s look at s1, s2 and s3. Notice how in the projector we are given s1, s2 and s3 as the function arguments - in the order in which we supplied them. This is far better than my first example in the introduction where we had to nest function calls. It’s readable and concise.

In short: the projector function is passed, as arguments, the returned values of the selectors listed before it in the order in which they were listed.

The role of the projector function is a powerful one. We can ask for various state properties, anywhere within our state tree, we can derive, transform or combine data from the state slices passed to it and return this modified data as a single object - typically for component consumption. Again, it’s clean and concise - and this state logic is nowhere inside our components. Our components consume the state, that’s it.

Prior to creating getPizzaState, in order to get the pizza entities in our component we needed to do this:

this.store
  .select(fromStore.getProductsState)
  .map((state) => state.pizzas)
  .map((pizzas) => pizza.entities);

However with our new found getPizzaState friend, we can now knock out one of the map calls:

this.store.select(fromStore.getPizzas).map((pizzas) => pizza.entities);

You can likely guess how we can now complete our journey and reference those entities - but the way we gain access is a little different and typically begins in our reducer, let’s take a look:

// src/products/store/reducers/pizzas.reducer.ts
export interface PizzaState {
  entities: { [id: number]: Pizza };
  loaded: boolean;
  loading: boolean;
}

export const initialState: PizzaState = {
  entities: {},
  loaded: false,
  loading: false,
};

export function reducer(
  state = initialState,
  action: fromPizzas.PizzasAction
): PizzaState {
  // ... switches and stuff
}

export const getPizzasEntities = (state: PizzaState) => state.entities;
export const getPizzasLoading = (state: PizzaState) => state.loading;
export const getPizzasLoaded = (state: PizzaState) => state.loaded;

What’s important to notice in the PizzaState reducer is the functions that are exported at the bottom. These are state property selectors - pure functions. This is a great place to export all other properties of this state level so that we can easily compose them in the next level up - which already has access to them through an import statement.

Back in our top-level reducer file, index.ts, we are going to compose a selector that can finally return our beloved pizza entities:

// src/products/store/reducers/index.ts
import * as fromPizzas from './pizzas.reducer';

export const getProductsState =
  createFeatureSelector<ProductsState>('products');

export const getPizzaState = createSelector(
  getProductsState,
  (state: ProductsState) => state.pizzas
);

export const getPizzasEntities = createSelector(
  getPizzaState,
  fromPizzas.getPizzasEntities
);

We use fromPizzas.getPizzasEntities as the projector function of createSelector that will return a reference to the pizza property entities.

Can we drop that last .map() that was left in our component code?…

this.store.select(fromStore.getPizzas).map((pizzas) => pizza.entities);

Why certainly. We can now supply our entities:

this.store.select(fromStore.getPizzasEntities);

This will now return us something like:

{
  1: { name: 'Pizza 1', id: 1 },
  2: { name: 'Pizza 2', id: 2 },
}

This is great, and exactly what we needed. However with Angular, or any other framework/solution really, we should expect this data structure back as an array. In the case of Angular, we can then ship it nicely into ngFor.

Entities represent a way of normalising data structures by using unique IDs as references to them. It makes data look up very easy, fast, composable - but that’s a story for another post.

So what if we want to convert our entities-based selector into an array format, for consumption via ngFor? We can create another selector, and use the projector function to map our data structure across to an array, quite easily:

// src/products/store/reducers/index.ts

// ...
export const getPizzasEntities = createSelector(
  getPizzaState,
  fromPizzas.getPizzasEntities
);

export const getAllPizzas = createSelector(getPizzasEntities, (entities) => {
  return Object.keys(entities).map((id) => entities[id]);
});

This has several key benefits. In state management, we might want to lookup items via their ID (normalised into entities), and we can do so by referencing getPizzasEntities where for example we could pass a route param id into our selector, and return that single entity. No loops, no maps, just an object lookup. It might be that for some components, we actually want entities, and for some - like a list view - we’re more interested in the same data, but as an array!

Selectors are also memoised, which means they’re fast and only recalculate when required to do so.

With our mission complete, we can now pass that one selector into our store.select and we’re done:

// an array of pizzas, what else could you ever ask for?
this.store.select(fromStore.getAllPizzas);

Voilà!

Conclusion

Selectors are a somewhat complex beast to start grasping and using, I’d encourage you to take a look around my example NGRX application to see how things fit together in a bigger picture.

Selectors are how we compose state, via functions that reference various pieces of our data structure. We can then merge them, combine them, pluck properties from them and combine them with others (this is especially easy with entities and ids that allow us to grab properties from our state and bring them into a new selector to compose new state). Possibilities are endless and somewhat easy to manage. Once we’ve composed our data structure via a selector, we can ship it off to our component(s) for consumption.

Learn Angular the right way.

The most complete guide to learning Angular ever built.
Trusted by 82,951 students.

Todd Motto

with Todd Motto

Google Developer Expert icon Google Developer Expert

Related blogs 🚀

Free eBooks:

Angular Directives In-Depth eBook Cover

JavaScript Array Methods eBook Cover

NestJS Build a RESTful CRUD API eBook Cover