Follow this document up along with this demo ngrx-ducks-12. There you will find the entire code that is shown in this article.💎
Implement State Mutations
First create a file where the mutation logic for a certain state slice should live. Put selectors logic here as well.
Add a class that implements the needed Case Reducers, builds required selectors and triggers actions.
createDuck supports to create both triggering actions (for Effects) and mutating actions. Second parameter of createDuck is a Case Reducer. It has a similar style of creating a reducer which actually makes state mutations.
The decorator StoreFacade introduces a new mechanism how the Facade is connected with the Store.🚀
Create Reducer automatically
If you use Ducks there is no need to maintain switch-case statements. Now you can create the Reducer based on the Ducks you have created inside the Facade class.
import { createDuck, getReducer, StoreFacade } from '@ngrx-ducks/core';
@StoreFacade()
export class CounterFacade {
duck1 = createDuck('Duck 1', /* reducer function */);
duck2 = createDuck('Duck 2', /* reducer function */);
duck3 = createDuck('Duck 3', /* reducer function */);
}
// declare initial state and export counterReducer
const initialState = { count: 0 };
export const counterReducer = getReducer(initialState, CounterFacade);
Just created counterReducer is a variable which can be used in ngrx method combineReducersdoc. From that point our implementation of a reducer is finished. Example below shows us basic implementation.
Since the whole logic is implemented in a Facade (CounterFacade in our case) we only need to import that Facade.
🎉 The API allows you to create dynamic facades and your components do not even know that Redux is working behind the scenes. We only rely on the contracts that we use messaging and streams.
👓Thanks to TypeScript each method is strictly typed and the whole process of creating or dispatching Actions is transparent.
@Component({/* ... */})
export class CounterComponent implements OnInit {
/* ... */
ngOnInit() {
this.counter.loadCount.dispatch(5000);
// ^ only type number is allowed 🎉
// - dispatches action: type: '[Counter] Set initial value'
// payload: 5000
}
}
Use NgRx's selectors
Creating selectors is the same way as it's in NgRx.
import { bindSelectors, StoreFacade } from '@ngrx-ducks/core';
import * as selectors from './counter.selectors';
@StoreFacade()
export class CounterFacade {
select = bindSelectors(selectors);
}
You see .loadCount(5000); is a method as well. 🤯 This addition is very useful if you need to produce actions being returned by an Effect.
Effects
You may wonder if @ngrx-ducks can help you with actions being dispatched to an Effect. Short answer: Yes, it can.
Setup
Let's use createDuck again! createDuck is the only entry point you need to know to create and dispatch actions. It works with calling Effects as well. It works with Effects just like actions work with Effects in ngrx.
import { createDuck } from '@ngrx-ducks/core';
@StoreFacade()
export class CounterFacade {
readonly loadCount = createDuck('[Counter] Load Count', dispatch<number>());
// another action (called in Effect below)
override = createDuck('[Counter] Set value',
(state: CounterState, payload: number) => {
return { ...state, count: payload };
}
);
}
export const counterActions = getActions(CounterFacade);
Triggering an Effect
import { currentCount } from './counter.selectors';
@Component({
/* ... */
})
export class CounterComponent {
counter$: Observable<number>;
constructor(private counter: CounterFacade) {
// let's dispatch an action which is handled by an Effect
this.counter.loadCount.dispatch(10);
}
}
Inside Effects
The last question is how an Effect itself handles actions with ngrx-ducks. An Effect filters the action type first. Let NgRx do it!😁