Skip to content

Commit

Permalink
docs: add exported reducer function to createReducer examples
Browse files Browse the repository at this point in the history
Closes #1762
  • Loading branch information
brandonroberts committed Jun 6, 2019
1 parent dec4bfa commit d20221f
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 18 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>
35 changes: 26 additions & 9 deletions projects/ngrx.io/content/guide/store/reducers.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 d20221f

Please sign in to comment.