is built on RxJS and brings reactive programming principles
Advantages: It is commonly used to manage the state of an Angular application in a predictable manner.
single source of truth managed by a store.
Actions are dispatched to express state changes.
// employee.actions.ts
// Action to load employees
export const loadEmployees = createAction('[Employee] Load Employees');
// Action triggered on successful API response
export const loadEmployeesSuccess = createAction(
'[Employee] Load Employees Success',
props<{ employees: Employee[] }>()
);
// Action triggered on failure
export const loadEmployeesFailure = createAction(
'[Employee] Load Employees Failure',
props<{ error: string }>()
);
A reducer is a pure function that handles the logic to update the state based on the action.
// employee.reducer.ts
export interface EmployeeState {
employees: Employee[];
error: string | null;
}
const initialState: EmployeeState = {
employees: [],
error: null,
};
export const employeeReducer = createReducer(
initialState,
on(loadEmployeesSuccess, (state, { employees }) => ({
...state,
employees: employees,
error: null,
})),
on(loadEmployeesFailure, (state, { error }) => ({
...state,
error: error,
}))
);
Effects handle side effects like API calls that are triggered by actions and update the state asynchronously.
// employee.effects.ts
@Injectable()
export class EmployeeEffects {
constructor(
private actions$: Actions,
private employeeService: EmployeeService
) {}
loadEmployees$ = createEffect(() =>
this.actions$.pipe(
ofType(loadEmployees),
withLatestFrom(this.store.select(selectEmployees)),
filter(([action, employees]) => employees.length === 0), // only load if no employees
switchMap(() =>
this.employeeService.getEmployees().pipe(
map((employees) => loadEmployeesSuccess({ employees })),
catchError((error) => of(loadEmployeesFailure({ error: error.message })))
)
)
)
);
}
Selectors are used to query specific slices of state from the store.
// employee.selectors.ts
export const selectEmployeeState = createFeatureSelector<EmployeeState>('employees');
export const selectAllEmployees = createSelector(
selectEmployeeState,
(state: EmployeeState) => state.employees
);
export const selectEmployeeError = createSelector(
selectEmployeeState,
(state: EmployeeState) => state.error
);
@NgModule({
imports: [
StoreModule.forRoot({ employees: employeeReducer }),
EffectsModule.forRoot([EmployeeEffects])
]
})
export class AppModule {}
The store holds the entire state of the application, which can be accessed and modified using actions, reducers, and effects. ``` // employee.component.ts
@Component({ selector: ‘app-employee’, templateUrl: ‘./employee.component.html’ }) export class EmployeeComponent implements OnInit { employees$ = this.store.select(selectAllEmployees);
constructor(private store: Store) {}
ngOnInit() { //When dispatch called, action will be send this.store.dispatch(loadEmployees()); } } ```
npm install @ngrx/store @ngrx/effects @ngrx/entity @ngrx/store-devtools