Skip to content

Commit

Permalink
docs: add exported reducer function to createReducer examples (#1924)
Browse files Browse the repository at this point in the history
Closes #1762
  • Loading branch information
brandonroberts authored Jun 6, 2019
1 parent bb9add7 commit 82216a5
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 19 deletions.
19 changes: 14 additions & 5 deletions projects/ngrx.io/content/guide/entity/adapter.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ Returns the `initialState` for entity state based on the provided type. Addition
Usage:

<code-example header="user.reducer.ts">
import { createReducer } from '@ngrx/store';
import { Action, createReducer } from '@ngrx/store';
import { EntityState, EntityAdapter, createEntityAdapter } from '@ngrx/entity';

export interface User {
Expand All @@ -70,7 +70,11 @@ export const initialState: State = adapter.getInitialState({
selectedUserId: null,
});

export const reducer = createReducer(initialState);
const userReducer = createReducer(initialState);

export function reducer(state: State | undefined: action: Action) {
return userReducer(state, action);
}
</code-example>

## Adapter Collection Methods
Expand Down Expand Up @@ -122,7 +126,7 @@ export const clearUsers = createAction('[User/API] Clear Users');
</code-example>

<code-example header="user.reducer.ts">
import { createReducer, on } from '@ngrx/store';
import { Action, createReducer, on } from '@ngrx/store';
import { EntityState, EntityAdapter, createEntityAdapter } from '@ngrx/entity';
import { User } from '../models/user.model';
import * as UserActions from '../actions/user.actions';
Expand All @@ -139,7 +143,7 @@ export const initialState: State = adapter.getInitialState({
selectedUserId: null,
});

export const reducer = createReducer(
const userReducer = createReducer(
initialState,
on(UserActions.addUser, (state, { user }) => {
return adapter.addOne(user, state)
Expand Down Expand Up @@ -176,7 +180,12 @@ export const reducer = createReducer(
}),
on(UserActions.clearUsers, state => {
return adapter.removeAll({ ...state, selectedUserId: null });
}));
})
);

export function reducer(state: State | undefined: action: Action) {
return userReducer(state, action);
}

export const getSelectedUserId = (state: State) => state.selectedUserId;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ The entity adapter is only used to update the `EntityState` properties. The addi

<code-example header="user.reducer.ts">
import { EntityState, EntityAdapter, createEntityAdapter } from '@ngrx/entity';
import { createReducer, on } from '@ngrx/store';
import { Action, createReducer, on } from '@ngrx/store';
import { User } from '../models/user.model';
import * as UserActions from '../actions/user.actions';

Expand All @@ -56,7 +56,8 @@ export const initialState: State = adapter.getInitialState({
selectedUserId: null,
});

export const reducer = createReducer(initialState,
export const userReducer = createReducer(
initialState,
on(UserActions.selectUser, (state, { userId }) => {
return { ...state, selectedUserId: userId };
}),
Expand All @@ -65,4 +66,7 @@ export const reducer = createReducer(initialState,
})
);

export function reducer(state: State | undefined: action: Action) {
return userReducer(state, action);
}
</code-example>
37 changes: 27 additions & 10 deletions projects/ngrx.io/content/guide/store/reducers.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ 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 switch statement
- 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.
Expand All @@ -36,6 +36,7 @@ a shape for the piece of state.
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.

<code-example header="scoreboard.reducer.ts">
import { Action, createReducer, on } from '@ngrx/store';
import * as ScoreboardPageActions from '../actions/scoreboard-page.actions';

export interface State {
Expand Down Expand Up @@ -64,26 +65,42 @@ 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. Define a reducer function that handles the actions for managing the state of the scoreboard.
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.

<code-example header="scoreboard.reducer.ts">
export const reducer = createReducer(
initialScoreState,
const scoreboardReducer = createReducer(
initialState,
on(ScoreboardPageActions.homeScore, state => ({ ...state, home: state.home + 1 })),
on(ScoreboardPageActions.awayScore, state => ({ ...state, away: state.away + 1 })),
on(ScoreboardPageActions.resetScore, state => ({ home: 0, away: 0 }))
);

export function reducer(state: State | undefined: action: Action) {
return scoreboardReducer(state, action);
}
</code-example>

In the example above, the reducer is handling 3 actions: `[Scoreboard Page] Home Score`, `[Scoreboard Page] Away Score`, and `[Scoreboard Page] Reset`. 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.
<div class="alert is-important">

**Note:** The exported `reducer` function is necessary as [function calls are not supported](https://angular.io/guide/aot-compiler#function-calls-are-not-supported) by the AOT compiler.

</div>

In the example above, the reducer is handling 3 actions: `[Scoreboard Page] Home Score`, `[Scoreboard Page] Away Score`, and `[Scoreboard Page] Score Reset`. 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.

<div class="alert is-important">

**Note:** The [spread operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax) 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](https://lodash.com) and [immer](https://github.com/mweststrate/immer).

</div>

When an action is dispatched, _all registered reducers_ receive the action. Whether they handle the action is determined by the switch statement. For this reason, each switch statement _always_ includes a default case that returns the previous state when the reducer function doesn't need to handle the action.
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.

<div class="alert is-important">

**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](https://v7.ngrx.io/guide/store/reducers).

</div>

## Registering root state

Expand All @@ -92,11 +109,11 @@ The state of your application is defined as one large object. Registering reduce
<code-example header="app.module.ts">
import { NgModule } from '@angular/core';
import { StoreModule } from '@ngrx/store';
import { scoreboardReducer } from './reducers/scoreboard.reducer';
import * as fromScoreboard from './reducers/scoreboard.reducer';

@NgModule({
imports: [
StoreModule.forRoot({ game: scoreboardReducer })
StoreModule.forRoot({ game: fromScoreboard.reducer })
],
})
export class AppModule {}
Expand Down Expand Up @@ -134,11 +151,11 @@ Now use the `scoreboard` reducer with a feature `NgModule` named `ScoreboardModu
<code-example header="scoreboard.module.ts">
import { NgModule } from '@angular/core';
import { StoreModule } from '@ngrx/store';
import { scoreboardReducer } from './reducers/scoreboard.reducer';
import * as fromScoreboard from './reducers/scoreboard.reducer';

@NgModule({
imports: [
StoreModule.forFeature('game', scoreboardReducer)
StoreModule.forFeature('game', fromScoreboard.reducer)
],
})
export class ScoreboardModule {}
Expand Down
3 changes: 1 addition & 2 deletions projects/ngrx.io/content/navigation.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,7 @@
"SideNav": [
{
"url": "docs",
"title": "Docs",
"hidden": true
"title": "Introduction"
},
{
"title": "@ngrx/store",
Expand Down

0 comments on commit 82216a5

Please sign in to comment.