Let’s Talk About Select and Reselect in @ngrx/store

Netanel Basal
Netanel Basal
Published in
4 min readAug 29, 2017

--

Recently I released a new state management pattern. You should check it out.

In this article, we will learn about the select() operator and when it is better to use reselect when querying a normalized state.

Let’s start with a basic example, todos and visibility filter. Say we have a store that looks like this:

We want to show the user the list of todos according to the selected filter. To accomplish this, we need two things from our store, the selected filter, and all the todos.

Our store is observable, so we can subscribe to it to get the most recent value. Each time the value changes, we can get the new value and update the UI according to the new state.

Let’s start working without the select() operator so we can recognize the problem.

The problem in the above code is that each time the store value changes, we get a new value and the calcVisibleTodos() method runs.
If we change the current user, our store will change, and the calcVisibleTodos() will run even though the value of the todos or the visibility filter have not changed.

We only want to run the calcVisibleTodos() method if either the todos or the visibility filter changes. That’s where the select() operator comes to play.

The select() operator gives you an observable that calls distinctUntilChanged() internally, meaning it will only fire when the state actually changes. ( a reference check )

Let’s refactor our example to use the select() operator mixed with combineLatest.

In the above case, the selector function ( i.e., calcVisibleTodos()) only runs if todos or visibility filters change. So, if we change an unrelated part in our state, like the current user, we will see that calcVisibleTodos() is not running.

Now that we understand the select() operator, let’s talk about reselect and when it is better to use that.

If you are familiar with the official ngrx example repository, you have probably noticed heavy use of the reselect library. Let’s look at how we can write the above example with reselect.

The above code gives you the same result as the combineLatest example we just saw.

In both cases, if the reference of todos or the visibilityFilter does not change, the selector function will NOT run.

At this moment you are likely asking why we need reselect. To answer that question, let’s see an example of a normalized state.

If we want to display the todos list, we first need to denormalized the todos, and then filter the visible todos according to the selected filter.

Imagine that you need to add a new todo. You need to update both the state.todos.entities and the state.todos.result objects.

Because we need to use combineLatest, which will call the selector function if either of the inputs have changed, this will cause the visibleTodos$ observable to fire twice and, as a result, the calcVisibleTodos() method will run twice.

Now Let’s see the same case with reselect, following the ngrx example pattern.

And in our component.

Because in this case we are using reselect and not combineLatest, the result is one call to the calcVisibleTodos() function.

Summary

We’ve seen that in most cases, using select() with combineLatest will suffice, but there are special cases like normalized state where reselect is a better choice to avoid unnecessary calculations.

I guess the ngrx team went all the way with reselect to maintain uniformity, although I find in many cases this is overkill.

Follow me on Medium or Twitter to read more about Angular, Vue and JS!

--

--

A FrontEnd Tech Lead, blogger, and open source maintainer. The founder of ngneat, husband and father.