Saghi Shiri
Saghi Shiri

Reputation: 617

Access to other state parts in reducer in ngrx

I need to Remove a state value if another state part has changed. How should I access to other store parts from a reducer in NgRx?

my store schema:

a: {...}
b: {...}

I need to access from a reducer with 'a' feature store to 'b' feature store which is initialized in another reducer.

Upvotes: 7

Views: 7185

Answers (2)

Thierry Falvo
Thierry Falvo

Reputation: 6290

Inside a reducer function, you can't access the state of another feature. This is normal and a good thing. Reducer are pure function.

As official doc mentioned :

Reducers in NgRx are responsible for handling transitions from one state to the next state in your application. Reducer functions handle these transitions by determining which actions to handle based on the action's type.

Despite of that consideration, it's technically possible to :

  • handle an action declared in feature B inside your reducer in feature A.
  • set an effect for an action declared in feature B inside your feature A.

But be careful, even if it's possible, you should take care of your architecture, to avoid issues and ensure maintainability.

I need to Remove a state value if another state part has changed. How should I access to other store parts from a reducer in NgRx?

In this case, I recommend you to dispatch a new action to update state of feature B, when an action from feature A occurs, via an effect.

Let's take an example:

Feature User

  • Action UserActions.Load is dispatched.
  • In User reducer, userState.loading set to true
  • An effect in UserEffects, handle UserActions.Load and dispatch a new action UIActions.ShowNotification.

Feature UI

  • In UI reducer, uiState.message set to ... received from UIActions.ShowNotification(...)
  • An effect in UIEffects, handle UIActions.ShowNotification to show a snackbar...

Note that this won't work if your feature are in lazy loaded modules.

However, be very careful with this type of architecture to keep your code maintainable and well organized. Sometimes it is necessary to create another parent state or use root state. (exactly as in the case of circular dependencies).

Otherwise, note that you can also compose selectors for several features, thus avoiding modifying each state. (keep single source of truth)

export const selectMessage = createSelector(
  selectLoading,  // from UserState feature
  selectMessage,  // from UiState feature
  (loading, message) => {
    return loading ? 'Loading...' : message;
  }
)

Upvotes: 5

daddykom
daddykom

Reputation: 220

You resolve that with @ngrx/effects.

An Effect spies the action stream and filters for an action and react on it.

Your filter in the effect for the action, which changes the related part and dispatch an other action which changes the other part of the store. (you only have to return the new action to the effect)

@Injectable()
export class MovieEffects {

  loadMovies$ = createEffect(() => this.actions$.pipe(
    ofType(ActionsOfA.ChangeAction),    // which changes the related part
    map(action=>ActionsOfB.RemoveOtherPart  // dispatch
    )
  );

const aReducer = createReducer(initialState,
  on((ActionsOfA.ChangeAction, {payload}), state => ({...state, a: payload}))
);

const bReducer = createReducer(initialState,
  on(ActionsOfB.RemoveOtherPart, state => ({...state, b:{} )
);

In the filtered action there is also the payload, if you need it..

Upvotes: 3

Related Questions