NgRx状态管理 #
一、NgRx概述 #
NgRx是基于Redux模式的状态管理库,用于管理Angular应用的状态。
text
┌─────────────────────────────────────────────────────────────┐
│ NgRx 数据流 │
├─────────────────────────────────────────────────────────────┤
│ │
│ Component → dispatch → Action → Reducer → Store │
│ ↑ ↓ │
│ └─────────────── select ──────────────────┘ │
│ │
│ Component → dispatch → Action → Effects → Action │
│ │
└─────────────────────────────────────────────────────────────┘
二、核心概念 #
| 概念 | 说明 |
|---|---|
| Store | 状态存储 |
| State | 应用状态 |
| Actions | 描述发生的事件 |
| Reducers | 纯函数,处理状态变化 |
| Selectors | 查询状态的纯函数 |
| Effects | 处理副作用 |
三、安装NgRx #
bash
npm install @ngrx/store @ngrx/effects @ngrx/store-devtools
四、定义状态 #
4.1 状态接口 #
typescript
export interface User {
id: number;
name: string;
email: string;
}
export interface UserState {
users: User[];
selectedUserId: number | null;
loading: boolean;
error: string | null;
}
export const initialUserState: UserState = {
users: [],
selectedUserId: null,
loading: false,
error: null
};
4.2 根状态 #
typescript
export interface AppState {
users: UserState;
products: ProductState;
}
五、Actions #
5.1 创建Actions #
typescript
import { createAction, props } from '@ngrx/store';
export const loadUsers = createAction('[User] Load Users');
export const loadUsersSuccess = createAction('[User] Load Users Success', props<{ users: User[] }>());
export const loadUsersFailure = createAction('[User] Load Users Failure', props<{ error: string }>());
export const selectUser = createAction('[User] Select User', props<{ userId: number }>());
export const addUser = createAction('[User] Add User', props<{ user: User }>());
export const updateUser = createAction('[User] Update User', props<{ user: User }>());
export const deleteUser = createAction('[User] Delete User', props<{ userId: number }>());
5.2 createActionGroup #
typescript
import { createActionGroup, props, emptyProps } from '@ngrx/store';
export const UserActions = createActionGroup({
source: 'User',
events: {
'Load Users': emptyProps(),
'Load Users Success': props<{ users: User[] }>(),
'Load Users Failure': props<{ error: string }>(),
'Select User': props<{ userId: number }>(),
'Add User': props<{ user: User }>(),
'Update User': props<{ user: User }>(),
'Delete User': props<{ userId: number }>()
}
});
六、Reducers #
6.1 创建Reducer #
typescript
import { createReducer, on } from '@ngrx/store';
import { UserActions } from './user.actions';
import { initialUserState, UserState } from './user.state';
export const userReducer = createReducer(
initialUserState,
on(UserActions.loadUsers, (state) => ({
...state,
loading: true,
error: null
})),
on(UserActions.loadUsersSuccess, (state, { users }) => ({
...state,
users,
loading: false
})),
on(UserActions.loadUsersFailure, (state, { error }) => ({
...state,
loading: false,
error
})),
on(UserActions.selectUser, (state, { userId }) => ({
...state,
selectedUserId: userId
})),
on(UserActions.addUser, (state, { user }) => ({
...state,
users: [...state.users, user]
})),
on(UserActions.updateUser, (state, { user }) => ({
...state,
users: state.users.map(u => u.id === user.id ? user : u)
})),
on(UserActions.deleteUser, (state, { userId }) => ({
...state,
users: state.users.filter(u => u.id !== userId)
}))
);
七、Selectors #
7.1 创建Selectors #
typescript
import { createFeatureSelector, createSelector } from '@ngrx/store';
import { UserState } from './user.state';
export const selectUserState = createFeatureSelector<UserState>('users');
export const selectUsers = createSelector(
selectUserState,
(state: UserState) => state.users
);
export const selectLoading = createSelector(
selectUserState,
(state: UserState) => state.loading
);
export const selectError = createSelector(
selectUserState,
(state: UserState) => state.error
);
export const selectSelectedUserId = createSelector(
selectUserState,
(state: UserState) => state.selectedUserId
);
export const selectSelectedUser = createSelector(
selectUsers,
selectSelectedUserId,
(users, selectedUserId) => users.find(u => u.id === selectedUserId) || null
);
export const selectUserById = (id: number) => createSelector(
selectUsers,
(users) => users.find(u => u.id === id)
);
八、Effects #
8.1 创建Effects #
typescript
import { Injectable } from '@angular/core';
import { Actions, createEffect, ofType } from '@ngrx/effects';
import { UserService } from '../services/user.service';
import { UserActions } from './user.actions';
import { catchError, map, mergeMap, switchMap } from 'rxjs/operators';
import { of } from 'rxjs';
@Injectable()
export class UserEffects {
constructor(
private actions$: Actions,
private userService: UserService
) {}
loadUsers$ = createEffect(() =>
this.actions$.pipe(
ofType(UserActions.loadUsers),
switchMap(() =>
this.userService.getUsers().pipe(
map(users => UserActions.loadUsersSuccess({ users })),
catchError(error => of(UserActions.loadUsersFailure({ error: error.message })))
)
)
)
);
addUser$ = createEffect(() =>
this.actions$.pipe(
ofType(UserActions.addUser),
mergeMap(({ user }) =>
this.userService.createUser(user).pipe(
map(createdUser => UserActions.addUserSuccess({ user: createdUser })),
catchError(error => of(UserActions.addUserFailure({ error: error.message })))
)
)
)
);
updateUser$ = createEffect(() =>
this.actions$.pipe(
ofType(UserActions.updateUser),
mergeMap(({ user }) =>
this.userService.updateUser(user).pipe(
map(updatedUser => UserActions.updateUserSuccess({ user: updatedUser })),
catchError(error => of(UserActions.updateUserFailure({ error: error.message })))
)
)
)
);
deleteUser$ = createEffect(() =>
this.actions$.pipe(
ofType(UserActions.deleteUser),
mergeMap(({ userId }) =>
this.userService.deleteUser(userId).pipe(
map(() => UserActions.deleteUserSuccess({ userId })),
catchError(error => of(UserActions.deleteUserFailure({ error: error.message })))
)
)
)
);
}
九、注册Store #
9.1 提供Store #
typescript
import { provideStore } from '@ngrx/store';
import { provideEffects } from '@ngrx/effects';
import { provideStoreDevtools } from '@ngrx/store-devtools';
import { userReducer } from './store/user.reducer';
import { UserEffects } from './store/user.effects';
export const appConfig: ApplicationConfig = {
providers: [
provideStore({
users: userReducer
}),
provideEffects([UserEffects]),
provideStoreDevtools({
maxAge: 25,
logOnly: !isDevMode()
})
]
};
十、在组件中使用 #
10.1 读取状态 #
typescript
import { Component, OnInit } from '@angular/core';
import { Store } from '@ngrx/store';
import { Observable } from 'rxjs';
import { UserActions } from '../store/user.actions';
import { selectUsers, selectLoading, selectError } from '../store/user.selectors';
@Component({
selector: 'app-user-list',
template: `
<div *ngIf="loading$ | async">加载中...</div>
<div *ngIf="error$ | async as error" class="error">{{ error }}</div>
<ul>
<li *ngFor="let user of users$ | async">
{{ user.name }} - {{ user.email }}
</li>
</ul>
`
})
export class UserListComponent implements OnInit {
users$: Observable<User[]>;
loading$: Observable<boolean>;
error$: Observable<string | null>;
constructor(private store: Store) {
this.users$ = this.store.select(selectUsers);
this.loading$ = this.store.select(selectLoading);
this.error$ = this.store.select(selectError);
}
ngOnInit() {
this.store.dispatch(UserActions.loadUsers());
}
}
10.2 派发Actions #
typescript
import { Component } from '@angular/core';
import { Store } from '@ngrx/store';
import { UserActions } from '../store/user.actions';
@Component({
selector: 'app-user-form',
template: `
<form (ngSubmit)="onSubmit()">
<input [(ngModel)]="user.name" name="name" />
<input [(ngModel)]="user.email" name="email" />
<button type="submit">添加用户</button>
</form>
`
})
export class UserFormComponent {
user = { name: '', email: '' };
constructor(private store: Store) {}
onSubmit() {
this.store.dispatch(UserActions.addUser({
user: {
id: Date.now(),
...this.user
}
}));
this.user = { name: '', email: '' };
}
}
10.3 使用信号(Angular 17+) #
typescript
import { Component, inject } from '@angular/core';
import { Store } from '@ngrx/store';
import { toSignal } from '@angular/core/rxjs-interop';
import { UserActions } from '../store/user.actions';
import { selectUsers, selectLoading } from '../store/user.selectors';
@Component({
selector: 'app-user-list',
template: `
<div *ngIf="loading()">加载中...</div>
<ul>
<li *ngFor="let user of users()">
{{ user.name }}
</li>
</ul>
`
})
export class UserListComponent {
private store = inject(Store);
users = toSignal(this.store.select(selectUsers), { initialValue: [] });
loading = toSignal(this.store.select(selectLoading), { initialValue: false });
constructor() {
this.store.dispatch(UserActions.loadUsers());
}
}
十一、完整示例 #
11.1 文件结构 #
text
store/
├── user/
│ ├── user.actions.ts
│ ├── user.reducer.ts
│ ├── user.selectors.ts
│ ├── user.effects.ts
│ └── user.state.ts
└── index.ts
11.2 完整状态管理 #
typescript
// user.state.ts
export interface UserState {
users: User[];
loading: boolean;
error: string | null;
}
export const initialUserState: UserState = {
users: [],
loading: false,
error: null
};
// user.actions.ts
export const UserActions = createActionGroup({
source: 'User',
events: {
'Load Users': emptyProps(),
'Load Users Success': props<{ users: User[] }>(),
'Load Users Failure': props<{ error: string }>(),
'Add User': props<{ user: User }>(),
'Add User Success': props<{ user: User }>(),
'Add User Failure': props<{ error: string }>(),
'Update User': props<{ user: User }>(),
'Update User Success': props<{ user: User }>(),
'Update User Failure': props<{ error: string }>(),
'Delete User': props<{ userId: number }>(),
'Delete User Success': props<{ userId: number }>(),
'Delete User Failure': props<{ error: string }>()
}
});
// user.reducer.ts
export const userReducer = createReducer(
initialUserState,
on(UserActions.loadUsers, (state) => ({
...state,
loading: true,
error: null
})),
on(UserActions.loadUsersSuccess, (state, { users }) => ({
...state,
users,
loading: false
})),
on(UserActions.loadUsersFailure, (state, { error }) => ({
...state,
loading: false,
error
})),
on(UserActions.addUserSuccess, (state, { user }) => ({
...state,
users: [...state.users, user]
})),
on(UserActions.updateUserSuccess, (state, { user }) => ({
...state,
users: state.users.map(u => u.id === user.id ? user : u)
})),
on(UserActions.deleteUserSuccess, (state, { userId }) => ({
...state,
users: state.users.filter(u => u.id !== userId)
}))
);
// user.selectors.ts
export const selectUserState = createFeatureSelector<UserState>('users');
export const selectAllUsers = createSelector(
selectUserState,
(state) => state.users
);
export const selectUserLoading = createSelector(
selectUserState,
(state) => state.loading
);
export const selectUserError = createSelector(
selectUserState,
(state) => state.error
);
// user.effects.ts
@Injectable()
export class UserEffects {
loadUsers$ = createEffect(() =>
this.actions$.pipe(
ofType(UserActions.loadUsers),
switchMap(() =>
this.userService.getUsers().pipe(
map(users => UserActions.loadUsersSuccess({ users })),
catchError(error => of(UserActions.loadUsersFailure({ error: error.message })))
)
)
)
);
constructor(
private actions$: Actions,
private userService: UserService
) {}
}
十二、总结 #
| 概念 | 说明 |
|---|---|
| Store | 状态存储 |
| State | 应用状态 |
| Actions | 描述事件 |
| Reducers | 处理状态变化 |
| Selectors | 查询状态 |
| Effects | 处理副作用 |
下一步:管道
最后更新:2026-03-26