Image from ngrx.io

References in NgRx Matter

George Kamtziridis
fromScratch Studio

--

Disclaimer: the current article assumes that the reader is aware of the fundamentals of the NgRx library as well as the basics of Angular.

NgRx is a state management system that is used in Angular applications. It is inspired by the Redux library and its purpose is to provide optimized and easy to use methods for manipulating the state of an Angular application.

The core functionality of the NgRx library lies in the Store. Essentially, the Store is a container that encapsulates the state of the app. The exact structure of the state is basically a JSON object. There are 3 primary concepts concerning the data flow between the Store and the main application:

  1. Actions: events that are dispatched from the main application, for instance components.
  2. Reducers: pure functions that alter the state of the Store.
  3. Selectors: pure functions that select object slices from the Store.

Fundamentally, the data flow can be summarized by the next 4 operations:

  1. Components listen to changes in some slices of the state via selectors.
  2. Components dispatch events via actions to request data changes.
  3. Reducers are triggered by the actions and modify the state accordingly.
  4. Components receive the new modified data.

The aforementioned flow seems to be pretty clear. However, it can be easily disrupted by seemingly insignificant programming decisions. The aftereffects of such decisions are not always apparent and this is precisely the reason why those problems are very dangerous. These issues might exist for a long time until someone realizes that something is not quite right. Even then, it will be difficult to determine which part of the application is underfunctioning.

The bad news is that most of the NgRx tutorials found online do not cover those details. At fromScratch Studio, we care about even the slightest details, so we’ve decided to publicly address those issues.

In this particular article we are going to take a look at the problems that arise from the improper usage of object references in an Angular/NgRx application, as well as how we can tackle those issues.

Setting up the codebase

To demonstrate the problems and the solutions, let’s assume we initially have the following code base:

We have a simple users property inside of our state. The users property is an object where each key is a user id, while each value contains some information about that particular user. We have, also, created a selector which will return the users slice of the state. In the app.component we make use of the select function to get the latest users data.

Issues

Selectors returning store references

When a selector returns a store reference, we must be really careful with it. Above, in the example, we receive the users object. If we only want to read it, then there shouldn’t be any problem at all. However, modifying the object directly will result in an unintentional state change.

By doing so, we’re altering the state in a way we shouldn’t. Essentially, we’re breaking the data flow and violating the cause and effect pattern we promised to follow. Remember that only the reducers must be able to change the state, nothing else.

Someone could propose the following solution:

By using the spread operator, we create a new reference, so the problem goes away, right? Well, actually no. It’s true that we create a new users reference, but remember that we’re altering an inner reference. The spread operator does not create new references for any of the objects that might be living inside of the outer object. So, in our case by using the spread operator, we achieve nothing.

One proper solution would be to use the cloneDeep lodash function, which creates references for every nested object.

Not updating references

Let’s say that we want a way to change the name of one of the users stored in the state. Of course, to do that, we need the following:

When the updateName function is called, the name of the user with the given id is updated. We can verify that by checking the allUsersSelector subscription. What if we want to watch only for a specific user? To achieve that, we need the following custom selector:

Someone could say that the IUser structure is an object that does not contain any other object, so we could use the spread operator instead of the cloneDeep function. Pay attention that the IUser structure contains an array (favorites). Arrays in Javascript are objects too. So, if we want to be 100% sure that no one is going to break the pattern we must use the cloneDeep function.

If we trigger the updateName function by passing the ID1 as the id and the name of your liking, we expect to get the new user printed out in our specificUserSelector subscription. However, if we execute this we get no output. Why is that? Didn’t we update the name? Yes, we did. Did we do it properly? Not at all.

We updated the name by doing:

This, in fact, changed the name, but it did not change the user reference. This means that the specificUserSelector will not return the new object value because the object reference is the same as the previous one.

Selectors make use of a practice called memoization. This practice prevents the selector from recomputing the output when the input arguments haven’t changed. In the case of an object argument, to determine if it has been changed the selector checks the reference. The users argument of the specificUserSelector has the same reference. Also, the props cannot change since we can only set them once.

To properly update user’s name as well as the users object which is our main concern, we have to do something like this:

Basically, we’re creating a new reference for the updated user (this is not necessary in our case) and then we create a new users reference. The new users reference will trigger our selector and it will return the updated user data to our component.

Summary

One of the rudimentary things about Javascript is that it’s a hash based programming language. You often hear about Javascript’s OOP capabilities, but you might never have heard about its hash based nature. This kind of unawareness leads to the 2 issues we’ve discussed above. Having a pretty good understanding of how hashes and references work in Javascript can help you, not only to avoid those mistakes, but also realize its strengths and weaknesses as a language.

--

--

George Kamtziridis
fromScratch Studio

Full Stack Software Engineer and Data Scientist at fromScratch Studio. BEng, MEng (Electrical Engineering/Computer Engineering) MSc (Artificial Intelligence)