Reducers
Reducers in NgRx are responsible for handling transitions from one state to the next state in your application. Reducer functions handle these transitions by determining which actions to handle based on the action's type.
Introduction
Reducers are pure functions in that they produce the same output for a given input. They are without side effects and handle each state transition synchronously. Each reducer function takes the latest Action
dispatched, the current state, and determines whether to return a newly modified state or the original state. This guide shows you how to write reducer functions, register them in your Store
, and compose feature states.
The reducer function
There are a few consistent parts of every piece of state managed by a reducer.
An interface or type that defines the shape of the state.
The arguments including the initial state or current state and the current action.
The functions that handle state changes for their associated action(s).
Below is an example of a set of actions to handle the state of a scoreboard, and the associated reducer function.
First, define some actions for interacting with a piece of state.scoreboard-page.actions.ts
Next, create a reducer file that imports the actions and define a shape for the piece of state.
Defining the state shape
Each reducer function is a listener of actions. The scoreboard actions defined above describe the possible transitions handled by the reducer. Import multiple sets of actions to handle additional state transitions within a reducer.scoreboard.reducer.ts
You define the shape of the state according to what you are capturing, whether it be a single type such as a number, or a more complex object with multiple properties.
Setting the initial state
The initial state gives the state an initial value, or provides a value if the current state is undefined
. You set the initial state with defaults for your required state properties.
Create and export a variable to capture the initial state with one or more default values.scoreboard.reducer.ts
The initial values for the home
and away
properties of the state are 0.
Creating the reducer function
The reducer function's responsibility is to handle the state transitions in an immutable way. Create a reducer function that handles the actions for managing the state of the scoreboard using the createReducer
function.scoreboard.reducer.ts
Note: The exported reducer
function is necessary as function calls are not supported the View Engine AOT compiler. It is no longer required if you use the default Ivy AOT compiler (or JIT).
In the example above, the reducer is handling 4 actions: [Scoreboard Page] Home Score
, [Scoreboard Page] Away Score
, [Scoreboard Page] Score Reset
and [Scoreboard Page] Set Scores
. Each action is strongly-typed. Each action handles the state transition immutably. This means that the state transitions are not modifying the original state, but are returning a new state object using the spread operator. The spread syntax copies the properties from the current state into the object, creating a new reference. This ensures that a new state is produced with each change, preserving the purity of the change. This also promotes referential integrity, guaranteeing that the old reference was discarded when a state change occurred.
Note: The spread operator only does shallow copying and does not handle deeply nested objects. You need to copy each level in the object to ensure immutability. There are libraries that handle deep copying including lodash and immer.
When an action is dispatched, all registered reducers receive the action. Whether they handle the action is determined by the on
functions that associate one or more actions with a given state change.
Note: You can also write reducers using switch statements, which was the previously defined way before reducer creators were introduced in NgRx. If you are looking for examples of reducers using switch statements, visit the documentation for versions 7.x and prior.
Registering root state
The state of your application is defined as one large object. Registering reducer functions to manage parts of your state only defines keys with associated values in the object. To register the global Store
within your application, use the StoreModule.forRoot()
method with a map of key/value pairs that define your state. The StoreModule.forRoot()
registers the global providers for your application, including the Store
service you inject into your components and services to dispatch actions and select pieces of state.app.module.ts
Registering states with StoreModule.forRoot()
ensures that the states are defined upon application startup. In general, you register root states that always need to be available to all areas of your application immediately.
Last updated