Ngrx Love
What?
- "RxJS powered state management for Angular applications, inspired by Redux"
- Focus on Redux part today
- RxJS will have to be a separate talk
Terms
- State - A single immutable data structure
- Actions - Objects that describe state changes
- Reducers - Pure functions that take the previous state and the next action to compute the new state
-
Store - An observable of state and an observer of actions
- Can dispatch actions
- Can select state
Redux
Redux
Why?
- Single source of truth
- One place to trigger actions (store.dispatch)
- easy to track
- One place to wire actions together (ngrx/effects)
- One place to modify state (reducers)
- easy to test
- One place to listen to state changes (store.select)
- One place to trigger actions (store.dispatch)
- Like a database for the browser
- More later...
How?
Make Way for Ducklings
- Apologies to Robert McCloskey
Mr. Mallard Alone
mister.component
calls store.dispatch(quack)
export class MisterComponent implements OnInit {
constructor(private store: Store<fromRoot.State>) { }
onClick() {
this.store.dispatch({ type: actionTypes.QUACK });
}
}
Mr. Mallard Alone
mister.reducer
updates the state
export const reducer = handleActions<State>(
{
[actionTypes.QUACK]: state => {
return Object.assign({}, state, { says: 'Quack!' });
},
},
initialState);
Mr. Mallard Alone
mister.component
renders new state
export class MisterComponent implements OnInit {
says$: Observable<string>;
constructor(private store: Store<fromRoot.State>) { }
ngOnInit() {
this.says$ = this.store.select(s => s.mister.says);
}
}
<figure>
<img src="assets/mister.png">
<figcaption>
<h2>{{says$ | async}}</h2>
</figcaption>
</figure>
Mrs. Mallard
missus.reducer
responds to mister's QUACK action
export const reducer = handleActions<State>(
{
[misterActionTypes.QUACK]: state => {
return Object.assign({}, state, { says: 'Quack!' });
},
},
initialState);
Ducklings!
duckling.effects connects actions together
@Effect()
misterQuack$: Observable<Action> = this.actions$
.ofType(misterActionTypes.QUACK)
.map(() => ducklingQuackAction(0));
@Effect()
ducklingQuack$: Observable<Action> = this.actions$
.ofType(actionTypes.QUACK).map(toPayload)
.withLatestFrom(
this.store.select(fromRoot.getDucklingState)
)
.filter(([ducklingIndex, ducklings])
=> ducklingIndex < ducklings.length - 1)
.map(([ducklingIndex])
=> ducklingQuackAction(ducklingIndex + 1))
.delay(400);
Ducklings!
missus.effects is overwhelmed
@Effect()
missusSquawk$: Observable<Action> = this.actions$
.ofType(misterActionTypes.QUACK)
.delay(4000)
.map(() => squawkAction());
Undo/Redo
Small changes for big wins
const reducers = {
duckling: fromDuckling.reducer,
missus: fromMissus.reducer,
mister: fromMister.reducer,
};
const reducer
= compose(undoable, combineReducers)(reducers);
@NgModule({
imports: [StoreModule.provideStore(reducer)],
})
export class AppModule { }
Why? (part 2)
Easy(er) Features
Redux Dev Tools Extension
- Benefit from the work of Redux/React community
- Easily view state
- Easily generate tests for reducers with real data
- Time travel
- Export/Import to reproduce bugs, etc.
More Questions
Ngrx Love
By Isaac Mann
Ngrx Love
- 585