Common and easy-to-make mistakes when you’re new to NgRx
This post is aimed at newcomers to NgRx.
In the first part of the post, we’ll be creating a new Angular project and we’ll implement a FizzBuzz implementation with NgRx. For the initial setup we’ll use the Angular CLI, and to scaffold the NgRx files we’ll be relying on the NgRx schematics.
The second part of the post will be used to refactor and reason about the implementation of the first part. The goal is to have a more maintainable code base.
What is FizzBuzz link
Before we start, let’s get everyone up to speed with FizzBuzz. FizzBuzz is a task that prints out numbers incrementally starting from 1, if the number is divisible by three the number get replaced by the word fizz and if the number is divisible by five it gets replaced by buzz. If the number is divisible by both three and five, the word fizz buzz will be printed out.
The output starts as follows:
1, 2, Fizz, 4, Buzz, Fizz, 7, 8, Fizz, Buzz, 11, Fizz, 13, 14, FizzBuzz, 16, 17, Fizz, 19, Buzz, Fizz, 22, 23, Fizz, Buzz, …
Initial project setup link
Let’s get started! We can create the project using the ng new
command and we’ll install the NgRx packages using the NgRx schematics. These schematics will install the package and will also automate the needed configurations to wire up NgRx correctly.
Creating the FizzBuzz reducer link
With the project ready, we can start to implement FizzBuzz. We do this by creating the fizzbuzz reducer, using the reducer schematic.
In the reducer:
- We define the fizzbuzz State, with the counter and the message to print out
- Provide an initial state, to initially load up the start of the application
- Create the reducer function. Inside this function, we modify the state in a pure way — we don’t mutate properties directly on the state. Whenever we want to print out the next output, invoked by the
NEXT
action, we increment the counter and create the message. Inside theswitch
statement, the default case is very important. When the reducer retrieves an action it isn’t responsible for (every reducer gets invoked by any action), we return the state as is. Without the default return case, the state would becomeundefined
.
Updating the component link
Now that we’ve defined the fizzbuzz state and the fizzbuzz reducer, it’s time to print out the fizzbuzz output. Inside the AppComponent
, we inject the Store to get the fizzbuzz message and to dispatch the NEXT
action to invoke the reducer.
To get the message from the store, we use the select
operator. Because State
is the whole application state, we first have to access the fizzbuzz
state in order to retrieve the message
. This gives us an RxJS stream, consisting of messages. To print the messages out, we use the Angular async
pipe.
To trigger a state change, we have to invoke the fizzbuzz reducer. Because we can’t invoke the reducer directly, we use the dispatch
function. We send the action NEXT
to the store which will invoke the fizzbuzz reducer, resulting in a new message in the message stream.
If we now start the application, we’ll see the fizzbuzz messages! 🎉
Refactor to a maintainable application link
Before we walk through the refactoring, I would encourage you to roll up your sleeves first and refactor the current code yourself on StackBlitz.
Using Actions and action creators link
The first step in the refactoring journey would be to create actions. We do this to remove magic strings, but maybe even more important, to allow action type checking. In this small example, the full power of this step won’t be visible. In larger applications, you’ll notice that TypeScript can infer the action’s properties inside the reducers.
We can create the action by using the action schematic.
The above schematic creates the action file and an example action inside of it, consisting out of an action enum, an action creator, and an action union type. We can modify this to fit our fizzbuzz application as follows:
Now, we can remove the magic string NEXT
inside the fizzbuzz reducer and inside the AppComponent
.
format_quoteFor more information about actions see the official docs and a previous post Let’s have a chat about Actions and Action Creators within NgRx.
Using selectors and derived state link
The problem with the working code is that we hold multiple versions of the same state inside the store state, this can make it hard to maintain over time when the application keeps on growing. That’s why we’re going to extract the fizzbuzz message inside a selector.
Before we can create the selector, we first have to provide a getter
to retrieve the counter
within the fizzbuzz state. When this is done, we can create our message selector to compute derived state.
format_quoteFor more info about selectors see the official docs and a previous post Sharing data between modules is peanuts.
The reducer link
With the action and the selector created, it’s time to clean up the reducer. This is done by:
- Typing the actions by using the actions union
FizzBuzzActions
. This makes sure that theswitch
statement’scase
s are valid and will also correctly type the action inside thecase
statement. - Replacing the
Next
string with the enumFizzBuzzActionTypes.Next
. - Removing the
message
property from the state, since the message is now derived in thegetMessage
selector.
format_quoteFor more info about reducers see the official docs and for more info about state normalization see a previous post Normalizing state.
Using effects link
I’m a big fan of effects and I’m using it to put every piece of logic that isn’t specific to the component’s logic. The most used and known example of this are AJAX requests, but the use cases of effects can be broadened out to everything that is causing your component to become impure.
Inside the effects/app.effects.ts
file created by the ng add
command, we’re going to move the logic to dispatch the Next
action on every second. For this, we’re using the RxJS interval
instead of the setInterval
method, creating a continuous stream that emits a Next
Action on each time interval. These actions will be dispatched to the store, invoking the reducer that on his turn triggers the getMessage
selector resulting in a re-render with the new message output.
format_quoteFor more info about effects see the official docs and for more effects usages see Start using ngrx/effects for this.
The new component link
With these steps completed, we can now go back to the AppComponent
and:
- Remove the dispatch logic
- Use the
getMessage
selector — personally, I also prefer removing the Store’s type. You won’t lose type safety because the selector (and its output) is typed. Plus from my experience, the Store’s type can lead to confusion, this is because at runtime it will contain the whole AppState.
The outcome link
If you keep these little tips in mind, each boundary inside the application has its own responsibility. This makes it easier to reason about and easier to maintain in the long run. If something goes wrong you can quickly scope it down to a specific boundary and you immediately know where to look.
Introducing Actions and Action Creators: reducing boilerplate link
- by using this convention, we get rid of a magic string value
- we gain type safety
- we don’t have to write an action more than once, we add a small layer of abstraction and follow the DRY principle
- it makes testing actions easier, we only have to write one test
- maintainability bonus: follow the Good Action Hygiene practice introduced by Mike Ryan
Reducers: only store the data once link
- this will lead to smaller and cleaner reducers
- we have a single point of truth, we don’t have to remember every state property for this state
- we only have to test state mutators
- readability bonus: if the immutable way (using the spread operator) of writing reducers is new and a bit uncomfortable, use Immer to make the transition easier — Clean NgRx reducers using Immer
Selectors: used to derive state based on the store state link
- don’t pollute the store state, use selectors as queries to create view models
- keep selectors small so they can be used to compose bigger selectors
- since these are just functions that receive state and return some data they are also easy to test
- selectors are highly performant because they memoize (cache) the latest execution so they don’t have to re-execute on every state change
- in-depth bonus: to fully understand the benefits that selectors provide see Selectors are more powerful than you think by Alex Okrushko
Effects: used for side effects link
- making your components pure, thus predictable, easier to reason about
- fully unleashes the power RxJS provides, e.g. cancel pending requests when needed
- easier to test components, but also provides separated tests for side effects
- architectural bonus: different ways to process actions, Patterns and Techniques by Victor Savkin
To end this post, see the this Blitz for the refactored version.
Outgoing links
- Sharing NgRx state between Angular modules is peanuts
- Let's have a chat about Actions and Action Creators within NgRx
- Start using ngrx/effects for this
- Clean NgRx reducers using Immer
- Normalizing state
Feel free to update this blog post on GitHub, thanks in advance!
Join My Newsletter (WIP)
Join my weekly newsletter to receive my latest blog posts and bits, directly in your inbox.
Support me
I appreciate it if you would support me if have you enjoyed this post and found it useful, thank you in advance.