Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Which to use StoreModule.forRoot or StoreModule.forFeature?

Tags:

angular

ngrx

I was reading about and they said that .forRoot merges all the reducers, and when manipulating the state in this way you are manipulating all the reducers at once.

Soon .forFeature allows you to manipulate each reducer as if it had a state for each one.

And I would like to know the following:

1) Is this true?

2) How to use .forFeature() with multiple reducers?

I'm getting the following error:

NullInjectorError: R3InjectorError(AppModule)[StoreFeatureModule -> ReducerManager -> ReducerManager -> ReducerManager]: 
  NullInjectorError: No provider for ReducerManager!

MODULE

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { HttpClientModule } from '@angular/common/http';

import { environment } from 'environments/environment';
import { StoreModule } from '@ngrx/store';
import { EffectsModule } from '@ngrx/effects';
import { StoreDevtoolsModule } from '@ngrx/store-devtools';
import { STORE_REDUCERS, STORE_EFFECTS } from './store';

import { AppComponentModule } from '@app/components/app-component.module';
import { AppComponent } from './app.component';

@NgModule({
    declarations: [AppComponent],
    imports: [
        BrowserModule,
        AppComponentModule,
        HttpClientModule,
        StoreDevtoolsModule.instrument({ maxAge: 25, logOnly: !environment.production }),
        StoreModule.forFeature('people', STORE_REDUCERS),
        EffectsModule.forFeature(STORE_EFFECTS),
    ],
    providers: [],
    bootstrap: [AppComponent],
})
export class AppModule {}

PEOPLE SELECTOR

import { createFeatureSelector, createSelector } from '@ngrx/store';
import { State, adapter } from './people.reducer';

const featureSelector = createFeatureSelector<State>('people');

export const { selectIds, selectEntities, selectAll, selectTotal } = adapter.getSelectors(featureSelector);

export const getLoading = createSelector(featureSelector, ({ loading }) => loading);
export const getError = createSelector(featureSelector, ({ error }) => error);
export const getMain = createSelector(selectAll, (peoples) => peoples.filter((people) => people.isMain)[0]);

STORE INDEX:

import { ActionReducerMap } from '@ngrx/store';
import * as fromPeopleReducer from './people/people.reducer';
import * as fromPeopleEffects from './people/people.effects';

export interface StoreState {
    people: fromPeopleReducer.State;
}

export const STORE_REDUCERS: ActionReducerMap<StoreState> = {
    people: fromPeopleReducer.reducer,
};

export const STORE_EFFECTS: any[] = [fromPeopleEffects.Effects];

1 Answers

.forFeature isn't a replacement for .forRoot, it's a way how to inject reducers and effects into the global store from lazy loaded modules or from imported libraries.

in the AppModule or in your root module you have to have at least

imports: [
  StoreModule.forRoot({}),
  EffectsModule.forRoot([]),
],

and then in any other imported module you can add something like

imports: [
  StoreModule.forFeature('addresses', reducers.addresses),
  StoreModule.forFeature('companies', reducers.companies),
  StoreModule.forFeature('users', reducers.users),
  EffectsModule.forFeature([EntityEffects]),
],

It means that addresses, companies, users features will be added to the global store. if you do it in several modules it still will use the same feature name, it doesn't create a store context per module. Reducers will be receiving all available actions in the app and effects too.

I would recommend to use .forFeature if you write a package with a library for ngrx or if its reducer / effect is useless without this module and its components.

like image 173
satanTime Avatar answered Jul 24 '26 21:07

satanTime



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!