Skip to content

Instantly share code, notes, and snippets.

@MikeRyanDev
Last active October 26, 2021 16:28
Show Gist options
  • Star 28 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save MikeRyanDev/5d361681ed0c81e38775dd2db15ae202 to your computer and use it in GitHub Desktop.
Save MikeRyanDev/5d361681ed0c81e38775dd2db15ae202 to your computer and use it in GitHub Desktop.

@ngrx/store v3

Problems:

  • Users want to compose reducer tree across modules
  • Idea of a single reducer function makes it difficult for the library to dynamically augment the shape of the state tree
  • Turning control over to the library to build the root reducer limits the use of meta-reducers
  • Feature modules may inadvertently collide with the state of the root module
  • Library authors may want to leverage @ngrx/store in their projects and provide an easy way for their libraries to work in apps already built with @ngrx/store

Proposal:

  • Add a fractal state mechanism for isolating child modules to @ngrx/store
  • Shift control over creating the root reducer function to @ngrx/store. Developers would have to provide an action reducer map instead of a root reducer.
  • Allow developers to optionally provide a reducerFactory function for turning an action reducer map into an action reducer.
  • Scope Store such that a child module's services and components only have access to the state of that module.
  • All actions continue to move through a global dispatcher. Every reducer continues to receive every action.
  • Add an API for dynamically inserting a reducer at a specific key
  • If a feature module is isolated (as in there is no parent module that uses @ngrx/store), intelligently work like a root module

At the core this is still redux making it easy to instrument, rehydrate, etc.

API:

Root module:

@NgModule({
  imports: [
    StoreModule.forRoot({ todos: todoReducer, layout: layoutReducer }, {
      initialState?: { ... },
      reducerFactory?: combineReducers
    })
  ]
})
export class AppModule { }

Global state shape:

{
  todos: { ... },
  layout: { ... }
}

Child module:

@NgModule({
  imports: [
    StoreModule.forFeature('feature', { blog: blogReducer }, {
      initialState?: { ... },
      reducerFactory?: combineReducers
    })
  ]
})
export class FeatureModule { }

New global state shape:

{
  todos: { ... },
  layout: { ... },
  feature: {
    blog: { ... }
  }
}

Injecting Store in a component or service belonging to FeatureModule only gives you access to the local state:

{
  blog: { ... }
}

Implementation

I have a rough but complete implementation available in a branch here: https://github.com/MikeRyan52/store/tree/feat/fractal-statemanagement

Prior Art:

@d3lm
Copy link

d3lm commented Nov 23, 2016

Pretty cool proposal. Although I would stick with forChild instead of forFeature, to be consistent with the Angular API. Why do you only provide local state to the feature? How do I get the global state then?

@hongbo-miao
Copy link

Thank you for all the work!

@afontaine
Copy link

Would not injecting a store module into a feature module still give that feature module's components and services access to the whole state?

@MarkPieszak
Copy link

Love to help with anything if you need some extra man-power! @MikeRyan52

@montella1507
Copy link

Your work is very appreciated! Thank you very much @MikeRyan52 , the idea behind this is great and it would be big advantage against standard Redux.

@maxime1992
Copy link

This is a really nice functionality, thanks for the great work @MikeRyan52 !
I've been looking for a milestone for V3 but couldn't find any.
Do you have any roadmap/date for V3 hitting master ? :)

@karolmie1
Copy link

karolmie1 commented Jan 4, 2017

@maxime1992 I guess they are waiting for angular4 and typescript 2.1 support: ngrx/store#269

@Alexandredc
Copy link

I have this TypeScript Error :

ERROR in [at-loader] node_modules/@ngrx/store/src/reducer-tree.ts:58:7 
    'root' implicitly has return type 'any' because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions.

I use forRoot like this :

StoreModule.forRoot<CoreState>({...})

Any idea ?

Thx

@Bretto
Copy link

Bretto commented Mar 23, 2017

Scope Store such that a child module's services and components only have access to the state of that module

Is there a way to circumvent this limitation ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment