Following is my angular app structure
app.module.ts
-- StoreModule.forRoot()
mainboard.module.ts
--StoreModule.forFeature('mainboard', reducer)
subFeature1.module.ts
subFeature2.module.ts
subFeature3.module.ts
dashboard1.module.ts
--StoreModule.forFeature('dashboard1', reducer)
subFeature1.module.ts
subFeature2.module.ts
subFeature3.module.ts
subFeature4.module.ts
subFeature5.module.ts
dashboard2.module.ts
--StoreModule.forFeature('dashboard2', reducer)
subFeature1.module.ts
subFeature2.module.ts
dashboard3.module.ts
--StoreModule.forFeature('dashboard3', reducer)
subFeature1.module.ts
subFeature2.module.ts
subFeature3.module.ts
subFeature4.module.ts
dashboard4.module.ts
--StoreModule.forFeature('dashboard4', reducer)
subFeature1.module.ts
subFeature2.module.ts
So the requirement is to have store for subfeatures.
Like this:
app.module.ts
-- StoreModule.forRoot()
mainboard.module.ts
--StoreModule.forFeature('mainboard', reducer)
subFeature1.module.ts
--StoreModule.forFeature('subFeature1', reducer)
subFeature2.module.ts
--StoreModule.forFeature('subFeature1', reducer)
subFeature3.module.ts
--StoreModule.forFeature('subFeature1', reducer)
...
HOW CAN I ACHIEIVE SUCH A HIERARCHY FOR MY NGRX/STORE ?
app.module is there all the dashboards are placed.
dashboard-x.modules are the places where all the navs/ items are placed.
and inside each dashboards sub features are included.
So my question is about how to register subFeatures with StoreModule.forFeature()?
Or do I need to make stores for each dashboards (StoreModule.forRoot()) and then StoreModule.forFeature() for each subFeatures? (if so then how may I be able to register it inside the app's StoreModule.forRoot())
NOTE
Currently I'm registering all of the subfeatures as forFeature.('subfeturename', reducer)
But the problem with this is that when I have a look at my state tree (Redux devtools) it is not following the above store tree structure. Since all the sub features are getting registered as forFeature() all of them are showing as properties (ie, they are not getting nested as expected). What I want instead is that I want to have them nested inside my state tree.
So if I have a look at my state tree I can see nested state Like this:
app
mainboard(open)
subFeature1
subFeature2
subFeature3
dashboard1(closed)
dashboard2(open)
subFeature1
subFeature2
dashboard3(closed)
dashboard4(closed)
//open and closed means expand and collapsed tree
Remember, each level (app > dashboard1 > subfeature) has different properties which needs to be managed by stores. So a store is necessary for each level.
There are three main building blocks of global state management with @ngrx/store : actions, reducers, and selectors. For a particular feature state, we create a reducer for handling state transitions based on the dispatched actions, and selectors to obtain slices of the feature state.
Where Does NgRx Store Data? NgRx stores the application state in an RxJS observable inside an Angular service called Store. At the same time, this service implements the Observable interface.
NgRx implements the Flux-Pattern. At a high level, it helps you unify all events and derive a common state in your Angular app. With NgRx, you store a single state and use actions to express state changes. It is ideal for apps with many user interactions and multiple data sources.
Redux is an application state manager for JavaScript applications, and keeps with the core principles of the Flux-architecture by having a unidirectional data flow in your application. Where Flux applications traditionally have multiple stores, Redux applications have only one global, read-only application state.
There are a lot of ways to do it. Below will be only my option based on my experience.
First thing to note -
forFeature
doesn't create an independent store context. It simply connects new reducers and effects on a later stage after a call offorRoot
and these effects and reducers will be getting all actions from the whole application, so if you sendupdateMyLazyFeature(123)
in one module - all modules that connected their reducers viaforFeature
will get this action and reduce it, and it's very easy to get data collisions, when you think that only your module will be updated but in reality it affects other parts of the application too.
When you want to split your store on independent features you need to review how a store feature is used. If the feature is used in more than 1 module, angular will put it in the shared file after build and it doesn't have any benifit compare to forRoot
.
if the feature is used in several modules - the best place would be to keep it in forRoot
.
If you use the feature only in a particular module and it's so tied that there's zero chance to use it anywhere else - then you can put it as a separate store feature.
One more case - if you implement a library with ngrx store, then you need to use
forFeature
because you can't accessforRoot
from your lib.
An example, we have a dashboard application and users have to login. It means that we should have access to user's data in several components, in this case the right place would be to put the feature in forRoot
.
Apart from that, we have a lazy registration module and a user can use an invitation link to prefill the form. Because the prefilled data won't be used anywhere else and the registration module is seldomly used too, we can implement the feature as forFeature
in the registration module. If user isn't going to register during his session - angular won't be going to load the module and the feature at all.
Also in the flux pattern it's important to keep data flat. The main problem flux and ngrx solve is data consistency and the easiest way to achieve it is to keep it flat.
Despite modules are nested like app -> mainboard -> subFeature1
etc, you still can add them to the top level of the store so they'll be stored on the same level as other reducers.
store
users
mainboard
dashboard2
subFeature1
subFeature2
subFeature3
There's no need to make it like
store
users
mainboard
subFeature1
subFeature2
subFeature3
dashboard2
subFeature1
subFeature2
subFeature3
It will bring complexity later when behavior of selectors, reducers and actions isn't so clear and predictable. With the flat structure you can move your feature to another project with ease.
Based on this info I would vote for the flat structure and would use
StoreModule.forFeature('logsControls', logsControls.reducer);
instead of
StoreModule.forFeature('dashboard1', {
logsControls: logsControls.reducer,
});
StoreModule.forFeature('dashboard2', {
logsControls: logsControls.reducer,
});
Because in the second case an action will update both dashboard1
and dashboard2
and requires a technique like correlationId
to affect only one feature, despite it looks like that dashboard1
and dashboard2
are different features and work independently, that's a wrong guess.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With