Employee Spotlight

NgRx Selectors — Blessing in Facade

Ksenia Sadrina
April 10, 2024
Utilize the power of NgRx selectors - learn how with anecdotes
In this article I rely on the fact that you are familiar with the following topics:
👉 Angular and RxJs.
👉 NgRx store — actions and reducers.


Congratulations, you’ve managed to create the perfect NgRx store with states, actions, reducers, and even effects. Nevertheless, something is still missing. It feels like every time you want to make a use of states in your angular components, you find yourself writing (or copy/pasting) the same selects over and over again.

This (and lots of other problems you are not even aware of), can be solved by using NgRx selectors. Combine the NgRx selectors with a store facade and you will get a true power couple.

Let’s say you have a grocery store that holds the state of your groceries:



Now, imagine that you need to use your vegetablesState in a restaurant component.

In addition, we have some conditions that should be met:

  1. The state should be initialized. ❗
  2. We want to select only the the following vegetables: 🍅 and 🥬.
  3. We want to mix our vegetables, prepare a salad, and publish a SaladPrepared action (which will trigger a reducer and save the salad in the store) 🥗.

To achieve the above goals, a minimal code should look like:


This will work, but imagine you’d like to use salad$ with other components. To do this, we will have to add the same chain of code into every component.

Do you understand where I’m going with this?

The Problem(s)

Bad for Performance

The computation inside the select() method is triggered after any component subscribes to the store. For instance, the action of filtering only the 🍅 and 🥬 vegetables will happen for every component, in spite of the fact that the results are the same.

This can get even worse in more complicated streams which combine store results with other observables.

DRY

Repeating yourself is always bad, and this case is not an exception. Let’s look at another component, home.component.ts. We want to prepare the same base salad at home, but this time, we want to add some 🧂 at the end of the preparation to give it some flavor.

This is how our home component would turn out:


Most of the salad preparation here is just the same as it is in the restaurant component.

Readability

Makes code look very cluttered and hard to maintain. RxJs is not making it easier on us with all the diagonal function chains, and those long selects are not helping either.

Testability

At anecdotes, we have a pretty strict approach to our unit-tests policy—every feature merged into the develop branch must include unit-tests. No doubt, it is much harder to create solid unit-tests fast for your component, when you have to write tests of the logic inside your select calls over and over again.

Store Facade to the Rescue

Store facade is a concept based on the “facade” design pattern. Its main purpose is “to hide” all the complex logic of NgRx under a readable and reusable API service. This will make things much more convenient for the developers working with your store.

To make it more visual, I took the liberty of adding store facade to the original NgRx lifecycle diagram (which can be found here).

As you can see, our component interacts only with the store facade. This, in contrast to the previous logic, in which the component interacted both with selectors (directly with store if you weren’t using selectors) and with actions. This is because the NgRx selectors were providing the data and actions were called when an action was taken by the user and needed to be updated in the store.

So how do we reach it?

  • Create a store facade service and inject it into the relevant components (instead of the regular store service).
  • Create a public getter (veggiesSalad$) and a private property (_veggiesSalad$) of the data you want to return. Why not just a getter? Since a getter will return a new instance of veggies salad to every subscribed component. Hence, the logic inside the map/NgRx filter functions will be executed just as many times. Therefore, we want to ensure that all this logic happens once and is shared between all the subscribed components.
  • Add shareReplay() operator to your observable! That way, the chain of operators will be invoked only on store change and not on every component subscription.
  • Add a separate method of publishVeggiesSalad(salad), which is much more readable than just this.store.dispatch(…) and can be reused.


Now you can consume your veggiesSalad$ from any component/service that you want, without the need of actually preparing it.

Our next step is to create asaltedSalad$, save it in the store facade and consume from everywhere we want. The best part is that we can use the veggiesSalad$ as a base salad.

Here is how it will look in the store facade:


And here is how small our ngOnInit() of the restaurant and home components became:


Improve It Even More with NgRx Selectors

Thanks to our new store facade, we’ve achieved the following goals:

✅ DRY

✅ Enhanced readability

✅ Increased testability — No example here (maybe in another blog post), but to sum it up — it’s much easier for developers to write tests. They just need to inject a “mock” store facade service into the tested component, rather than mocking the same logic for every component test.

✅ Improved performance — Thanks to saving veggiesSalad as a property on a single service and using shareReplay().

However, we still can improve all those points even more, bear with me just a little more!

“Selectors are pure functions used for obtaining slices of store state.” (ngrx official docs)

Basically they allow you to create reusable and smart select() functions. Those functions receive fragments of the store state as arguments, perform a certain logic inside, and return the result.

So what is so special about them?

They are capable of memorizing stuff! But what does that mean?

It means that the logic inside the selectors won’t be triggered unless the specific slice of state hasn’t changed. To explain it better, let’s take a closer look at the following example (we will use the example of veggiesSalad$ from before):

  • The selector function will be re-invoked only if vegetablesState changes. fruitState change won’t trigger it.
  • We’ve created 2 helper functions — each function returns a different slice of the state. Those functions can be later reused in other selectors. Make sure that they return values which are not re-computed with every invocation. For instance, don’t return *state*.*slice*.filter(...), since it will return a new reference to a new array with every call (even if the state hasn’t changed).
  • We’ve created the selector with the createSelector() function. This is the best place for your complicated logic.


The following code example is a bonus. In our store facade (the real one in anecdotes.ai), we check whether certain entities are initialized. To avoid adding filter() operator to every facade property, we’ve added a generic selectEntitiesAfterInit pipeable function. It receives the selector, filters the desired values, and returns it. There you can chain any operators you want.


Now, use the last specific pipeable function in the store facade (add it like an operator to the pipe of your store).

Now we have our reusable selector in place and we can use it wherever we want.

Conclusion

We learned why and how to utilize the power of NgRx selectors and store facade. We also witnessed some code examples which are just a taste of the awesome things you can do with them.

Let me know if you would be interested to learn about any ofthe the following topics:

👉 Integrating NgRx store into your project — from scratch

👉 Testing NgRx store with store facade

👉 NgRx selectors with web workers



Ksenia Sadrina
Full stack developer at anecdotes. Has a soft spot for front-end and fixing what’s broken.