nilsmehlhorn / ngrx-wieder

Lightweight undo-redo for Angular with NgRx & immer.js
https://nils-mehlhorn.de/posts/angular-undo-redo-ngrx-redux
MIT License
127 stars 10 forks source link

Using createUndoRedoReducer with StoreModule.forFeature doesn't work #40

Closed pareshchouhan closed 3 years ago

pareshchouhan commented 3 years ago

I followed the installation guide created a reducer for a feature instead of root and it didn't work, not getting any errors as well other than the one below, which is just error because the state doesn't get initialized and the selectors are trying to access the data.(ngrx-wieder tries to access the store as well and fails).

Doing nothing fancy here, just trying to create a reducer and use it with StoreModule.forFeature('databoard', undoableRedcucer)

If I use createReducer from ngrx/store It works fine.

error-handler.service.ts:23 Error: TypeError: Cannot read property 'histories' of undefined
    at ngrx-wieder.js:88
    at reducer (databoard.reducer.ts:289)
    at ngrx-store.js:290
    at combination (ngrx-store.js:245)
    at ngrx-store.js:752
    at ngrx-store.js:752
    at ngrx-store.js:278
    at computeNextEntry (ngrx-store-devtools.js:426)
    at recomputeStates (ngrx-store-devtools.js:460)
    at ngrx-store-devtools.js:734

ngrx-store.js:695 @ngrx/store: The feature name "databoard" does not exist in the state, therefore createFeatureSelector cannot access it.  Be sure it is imported in a loaded module using StoreModule.forRoot('databoard', ...) or StoreModule.forFeature('databoard', ...).  If the default state is intended to be undefined, as is the case with router state, this development-only warning message can be ignored.
(anonymous) @ ngrx-store.js:695
(anonymous) @ ngrx-store.js:571
defaultStateFn @ ngrx-store.js:571

databoard.reducer.ts:319 selectorState undefined
error-handler.service.ts:23 Error: TypeError: Cannot read property 'histories' of undefined
    at ngrx-wieder.js:88
    at reducer (databoard.reducer.ts:289)
    at ngrx-store.js:290
    at combination (ngrx-store.js:245)
    at ngrx-store.js:752
    at ngrx-store.js:752
    at ngrx-store.js:278
    at ngrx-store-devtools.js:737
    at Array.map (<anonymous>)
    at ngrx-store-devtools.js:737
davidveen commented 3 years ago

Unfortunately I'm having the same issue with a forFeature reducer. Did you have any luck solving it?

nilsmehlhorn commented 3 years ago

Can you show your code and/or provide a reproduction? Library, NgRx and Angular versions would also be helpful.

pareshchouhan commented 3 years ago

@nilsmehlhorn , Here's a sample project that doesn't work, https://github.com/pareshchouhan/weider-error

Reducer is here https://github.com/pareshchouhan/weider-error/blob/master/src/app/reducers/index.ts and it is used here https://github.com/pareshchouhan/weider-error/blob/master/src/app/app.module.ts

Same error as above.

"@ngrx/store": "^12.0.0",   
 "@ngrx/store-devtools": "^12.0.0",    
"immer": "^9.0.2",    
"ngrx-wieder": "^6.0.0"  
davidveen commented 3 years ago

fwiw, in my project I'm using

  "dependencies": {
    "@angular/animations": "~11.2.0",
    "@angular/cdk": "^11.0.3",
    "@angular/common": "~11.2.0",
    "@angular/compiler": "~11.2.0",
    "@angular/core": "~11.2.0",
    "@angular/forms": "~11.2.0",
    "@angular/material": "^11.0.3",
    "@angular/platform-browser": "~11.2.0",
    "@angular/platform-browser-dynamic": "~11.2.0",
    "@angular/router": "~11.2.0",
    "@ngrx/effects": "^11.0.0",
    "@ngrx/entity": "^11.0.0",
    "@ngrx/router-store": "^11.0.0",
    "@ngrx/schematics": "^11.0.0",
    "@ngrx/store": "^11.0.0",
    "@ngrx/store-devtools": "^11.0.0",
    "immer": "^9.0.2",
    "ngrx-store-freeze": "^0.2.4",
    "ngrx-wieder": "^6.0.0",
    ...
  },

Importing in a lazy loaded module

  imports: [
    ...
    StoreModule.forFeature(FEATURE_KEY, reducerMap),
    EffectsModule.forFeature([OrderOverviewEffects, OrderDetailEffects]),
  ],

The reducerMap:

export const reducerMap: ActionReducerMap<feature.OrdersState> = {
  orders: fromOrder.orderReducer,
  detail: fromDetail.orderDetailReducer,
  overview: fromOverview.orderOverviewReducer,
}

where only the orders reducer uses undoRedo

const { createUndoRedoReducer } = undoRedo({
  allowedActionTypes: [
    actions.updateOrderLine.type
  ]
})

export const orderReducer = createUndoRedoReducer<OrderState>(
  initialState,
  on(...)
)

I've been trying to debug, but the only warning it will give before erroring on the feature selector is that the initialState is not set for the feature slice (ordersModule). Works fine if I replace createUndoRedoReducer with createReducer.

@ngrx/store: The feature name "ordersModule" does not exist in the state, therefore createFeatureSelector cannot access it.  Be sure it is imported in a loaded module using StoreModule.forRoot('ordersModule', ...) or StoreModule.forFeature('ordersModule', ...).  If the default state is intended to be undefined, as is the case with router state, this development-only warning message can be ignored.
nilsmehlhorn commented 3 years ago

I was able to reproduce this in @pareshchouhan's project as well as in a test case. The PR #41 should fix the error.

Please checkout the corresponding branch locally, build the project with npm run build, then go to dist/ngrx-wieder and run npm link. Afterwards, you can go into your Angular projects and run npm link ngrx-wieder in the base directory. This makes your app use the local build. Then run your Angular app and verify if it's working. If you're done, you can unlink the package in the Angular project with npm unlink ngrx-wieder.

Hope this resolves the issue, I'll merge and release if it does.

pareshchouhan commented 3 years ago

@nilsmehlhorn , I have tested it out, everything seems to be working. Thanks for the quick turnaround, if there's any way I can help with the project let me know. :)

nilsmehlhorn commented 3 years ago

I've slightly adjusted the fix to listen for NgRx's INIT and UPDATE actions (it was just INIT before). Based on the test reproduction I've added it should still be fixed by that. I'll merge and release, please re-open the issue if I've broken the fix.

pareshchouhan commented 3 years ago

@nilsmehlhorn , same error with ngrx-wieder 6.0.1

core.js:5980 ERROR TypeError: Cannot read property 'histories' of undefined
    at ngrx-wieder.js:87
    at ngrx-store.js:290
    at combination (ngrx-store.js:245)
    at ngrx-store.js:752
    at ngrx-store.js:278
    at ngrx-store-devtools.js:747
    at Array.map (<anonymous>)
    at ngrx-store-devtools.js:747
    at ScanSubscriber.StoreDevtools.liftedAction$.pipe.Object.state [as accumulator] (ngrx-store-devtools.js:790)
    at ScanSubscriber._tryNext (scan.js:49)
nilsmehlhorn commented 3 years ago

Sorry for the delay on this one. I went back to the initial solution where I'm now calling the underlying reducer anytime the state is undefined. I thought it'd be better to do this only upon INIT and UPDATE, however, the library's meta-reducer depends on the state not being undefined so that it can track the undo-redo history - therefore this should also be fine.

https://github.com/nilsmehlhorn/ngrx-wieder/blob/06fdda47aa71d75928a7b837e1d1b8c5219046d1/projects/ngrx-wieder/src/lib/undo-redo.reducer.ts#L136

Honestly, I think that the bug has some relation to the NgRx dev-tools. You might want to try v6.0.1 without importing the StoreDevtoolsModule. I've tried that with your project @pareshchouhan and it worked. Debugging showed that the underlying reducer returns a proper state for INIT and/or UPDATE actions but that state is then gone when the reducer is invoked with the devtool's RECOMPUTE - that's weird.