Reputation: 617
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
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 :
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
UserActions.Load
is dispatched.User
reducer, userState.loading
set to true
UserEffects
, handle UserActions.Load
and dispatch a new action UIActions.ShowNotification
.Feature UI
UI
reducer, uiState.message
set to ...
received from UIActions.ShowNotification(...)
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
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