RxJS powered Redux state management for Angular applications
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Standalone providers enable modern Angular standalone application configuration using environment providers for root and feature state management without traditional NgModules.
Configures the root store for standalone Angular applications using environment providers.
/**
* Provides root store configuration for standalone applications
* @param reducers - Map of reducers or injection token for reducers
* @param config - Root store configuration options
* @returns EnvironmentProviders for root store setup
*/
function provideStore<T, V extends Action = Action>(
reducers?: ActionReducerMap<T, V> | InjectionToken<ActionReducerMap<T, V>>,
config?: RootStoreConfig<T, V>
): EnvironmentProviders;Usage Examples:
import { bootstrapApplication } from "@angular/platform-browser";
import { provideStore } from "@ngrx/store";
import { AppComponent } from "./app.component";
import { reducers, metaReducers } from "./app.reducers";
// Bootstrap standalone application with store
bootstrapApplication(AppComponent, {
providers: [
provideStore(reducers, {
metaReducers,
runtimeChecks: {
strictStateImmutability: true,
strictActionImmutability: true,
strictStateSerializability: true,
strictActionSerializability: true,
strictActionWithinNgZone: true,
strictActionTypeUniqueness: true
}
}),
// other providers
]
});
// Alternative with injection token
export const ROOT_REDUCERS = new InjectionToken<ActionReducerMap<AppState>>('Root Reducers');
bootstrapApplication(AppComponent, {
providers: [
{ provide: ROOT_REDUCERS, useValue: reducers },
provideStore(ROOT_REDUCERS, { metaReducers }),
]
});Provides feature state configuration for standalone applications and route-based lazy loading.
/**
* Provides feature state with reducer map
* @param featureName - Name/key for the feature state slice
* @param reducers - Feature reducers map
* @param config - Feature store configuration options
* @returns EnvironmentProviders for feature state
*/
function provideState<T, V extends Action = Action>(
featureName: string,
reducers: ActionReducerMap<T, V> | InjectionToken<ActionReducerMap<T, V>>,
config?: StoreConfig<T, V> | InjectionToken<StoreConfig<T, V>>
): EnvironmentProviders;
/**
* Provides feature state with single reducer
* @param featureName - Name/key for the feature state slice
* @param reducer - Single reducer function
* @param config - Feature store configuration options
* @returns EnvironmentProviders for feature state
*/
function provideState<T, V extends Action = Action>(
featureName: string,
reducer: ActionReducer<T, V> | InjectionToken<ActionReducer<T, V>>,
config?: StoreConfig<T, V> | InjectionToken<StoreConfig<T, V>>
): EnvironmentProviders;
/**
* Provides feature state using feature slice configuration
* @param slice - Complete feature slice configuration
* @returns EnvironmentProviders for feature state
*/
function provideState<T, V extends Action = Action>(
slice: FeatureSlice<T, V>
): EnvironmentProviders;Usage Examples:
import { provideState } from "@ngrx/store";
import { userReducer } from "./user.reducer";
import { orderReducer, orderMetaReducer } from "./order.reducer";
// Route-based feature providers
export const userRoutes: Route[] = [
{
path: '',
component: UserListComponent,
providers: [
// Single reducer feature
provideState('users', userReducer, {
initialState: { entities: [], loading: false, error: null }
})
]
}
];
export const orderRoutes: Route[] = [
{
path: '',
component: OrderDashboardComponent,
providers: [
// Reducer map feature
provideState('orders', {
list: orderListReducer,
details: orderDetailsReducer,
filters: orderFiltersReducer
}, {
metaReducers: [orderMetaReducer],
initialState: {
list: { entities: [], pagination: null },
details: null,
filters: { status: 'all', dateRange: null }
}
})
]
}
];
// Feature slice configuration
export const productRoutes: Route[] = [
{
path: '',
component: ProductCatalogComponent,
providers: [
provideState({
name: 'products',
reducer: productReducer,
initialState: initialProductState,
metaReducers: [productCacheMetaReducer]
})
]
}
];import { ApplicationConfig } from "@angular/core";
import { provideRouter } from "@angular/router";
import { provideStore } from "@ngrx/store";
import { routes } from "./app.routes";
import { reducers, metaReducers } from "./store";
export const appConfig: ApplicationConfig = {
providers: [
provideRouter(routes),
provideStore(reducers, {
metaReducers,
runtimeChecks: {
strictStateImmutability: true,
strictActionImmutability: true,
strictActionWithinNgZone: true
}
}),
// Additional providers
provideHttpClient(),
provideAnimations()
]
};
// Bootstrap application
import { bootstrapApplication } from "@angular/platform-browser";
import { AppComponent } from "./app.component";
bootstrapApplication(AppComponent, appConfig);import { Routes } from "@angular/router";
import { provideState } from "@ngrx/store";
export const routes: Routes = [
{
path: 'dashboard',
loadComponent: () => import('./dashboard/dashboard.component').then(c => c.DashboardComponent),
providers: [
provideState('dashboard', dashboardReducer, {
initialState: () => ({
widgets: getDefaultWidgets(),
layout: getUserLayout(),
preferences: getUserDashboardPreferences()
})
})
]
},
{
path: 'analytics',
loadChildren: () => import('./analytics/analytics.routes').then(r => r.analyticsRoutes),
providers: [
// Shared analytics state
provideState('analytics', analyticsReducer)
]
},
{
path: 'settings',
loadComponent: () => import('./settings/settings.component').then(c => c.SettingsComponent),
providers: [
provideState({
name: 'settings',
reducer: settingsReducer,
initialState: defaultSettings,
metaReducers: [settingsValidationMetaReducer]
})
]
}
];// analytics.routes.ts
import { Routes } from "@angular/router";
import { provideState } from "@ngrx/store";
export const analyticsRoutes: Routes = [
{
path: '',
component: AnalyticsOverviewComponent
},
{
path: 'reports',
component: ReportsComponent,
providers: [
// Feature-specific state for reports
provideState('reports', reportsReducer, {
initialState: {
templates: [],
generated: [],
filters: defaultReportFilters
}
})
]
},
{
path: 'metrics',
component: MetricsComponent,
providers: [
// Metrics-specific state
provideState('metrics', {
realtime: realtimeMetricsReducer,
historical: historicalMetricsReducer,
alerts: alertsReducer
})
]
}
];import { InjectionToken, inject } from "@angular/core";
import { UserService } from "./user.service";
// Injectable configuration tokens
export const USER_INITIAL_STATE = new InjectionToken<UserState>('User Initial State', {
providedIn: 'root',
factory: () => {
const userService = inject(UserService);
return {
profile: userService.getCachedProfile(),
preferences: userService.getStoredPreferences(),
permissions: userService.getPermissions()
};
}
});
export const USER_META_REDUCERS = new InjectionToken<MetaReducer<UserState>[]>('User Meta Reducers', {
providedIn: 'root',
factory: () => [
userAuditMetaReducer,
userValidationMetaReducer
]
});
// Route using injectable configuration
export const userRoutes: Routes = [
{
path: '',
component: UserProfileComponent,
providers: [
provideState('user', userReducer, {
initialState: USER_INITIAL_STATE,
metaReducers: USER_META_REDUCERS
})
]
}
];import { inject } from "@angular/core";
import { Store } from "@ngrx/store";
// Service for dynamic state management
export class DynamicStateService {
private store = inject(Store);
registerFeature<T>(name: string, reducer: ActionReducer<T>, initialState?: T) {
// Add reducer dynamically
this.store.addReducer(name, reducer);
// Initialize state if provided
if (initialState) {
this.store.dispatch({ type: `[${name}] Initialize`, payload: initialState });
}
}
unregisterFeature(name: string) {
this.store.removeReducer(name);
}
}
// Component using dynamic state
@Component({
selector: 'app-dynamic-feature',
providers: [DynamicStateService]
})
export class DynamicFeatureComponent {
private dynamicState = inject(DynamicStateService);
ngOnInit() {
// Register state when component initializes
this.dynamicState.registerFeature('temporaryFeature', temporaryReducer, {
data: [],
loading: false
});
}
ngOnDestroy() {
// Clean up when component is destroyed
this.dynamicState.unregisterFeature('temporaryFeature');
}
}// environment.ts
export const environment = {
production: false,
storeConfig: {
runtimeChecks: {
strictStateImmutability: true,
strictActionImmutability: true,
strictStateSerializability: true,
strictActionSerializability: true,
strictActionWithinNgZone: true,
strictActionTypeUniqueness: true
},
metaReducers: ['logger', 'freeze']
}
};
// environment.prod.ts
export const environment = {
production: true,
storeConfig: {
runtimeChecks: {
strictStateImmutability: false,
strictActionImmutability: false,
strictStateSerializability: false,
strictActionSerializability: false,
strictActionWithinNgZone: false,
strictActionTypeUniqueness: false
},
metaReducers: []
}
};
// app.config.ts
import { environment } from "../environments/environment";
const getMetaReducers = (): MetaReducer<AppState>[] => {
const reducers: MetaReducer<AppState>[] = [];
if (environment.storeConfig.metaReducers.includes('logger')) {
reducers.push(storeLogger);
}
if (environment.storeConfig.metaReducers.includes('freeze')) {
reducers.push(storeFreeze);
}
return reducers;
};
export const appConfig: ApplicationConfig = {
providers: [
provideStore(reducers, {
runtimeChecks: environment.storeConfig.runtimeChecks,
metaReducers: getMetaReducers()
})
]
};import { provideHttpClient, withInterceptors } from "@angular/common/http";
import { provideStore } from "@ngrx/store";
export const appConfig: ApplicationConfig = {
providers: [
provideHttpClient(withInterceptors([authInterceptor, errorInterceptor])),
provideStore(reducers, { metaReducers }),
// State that depends on HTTP client
provideState('auth', authReducer, {
initialState: () => ({
user: getStoredUser(),
token: getStoredToken(),
refreshToken: getStoredRefreshToken()
})
})
]
};import { provideRouter, withPreloading, PreloadAllModules } from "@angular/router";
import { provideStore, provideState } from "@ngrx/store";
export const appConfig: ApplicationConfig = {
providers: [
provideRouter(routes, withPreloading(PreloadAllModules)),
provideStore(reducers),
// Router-dependent state
provideState('navigation', navigationReducer, {
initialState: {
currentRoute: null,
previousRoute: null,
breadcrumbs: []
}
})
]
};provideState in route providers for lazy-loaded features