store: Problem with NgxsStoragePluginModule and NgxsReduxDevtoolsPluginModule together

Versions

* ngxs: 2.0.0-rc.24
* @ngxs/devtools-plugin: 2.0.0-rc.24
* @ngxs/storage-plugin: 2.0.0-rc.24
  • Configure my plugins:
 NgxsReduxDevtoolsPluginModule.forRoot({
      disabled: false
    }),
    NgxsStoragePluginModule.forRoot({
      key: '@@STATE',
      storage: StorageOption.LocalStorage,
      deserialize: JSON.parse,
      serialize: JSON.stringify
    })
  • Dispacht a accion that belongs to de NgxsModule.forRoot([AppState])
@Action(SearchPeoples)
  serarchPeoples({getState, setState}: StateContext<AppStateModel>) {
    const state = getState();
    return this.http.get<Search>('https://randomuser.me/api/?results=5').pipe(
      tap(value => {
        setState({
          ...state,
          search: value.results
        });
      })
    );
  }
search() {
    this.store.dispatch(new SearchPeoples()).subscribe(value => {
      console.log('The action is completed.');
    });
  }
  • Navigate to the feature module (lazy loading)

in the console show the error:

captura

because in the feature module, show a component that need featureSearch :

export class FooComponent {

  @Select(state => state.featureStage.featureSearch)
  result$: Observable<Result>;

  constructor(private store: Store) {
  }

  search() {
    this.store.dispatch(new SearchPeoplesFeature({countPeople: 8, test: true})).subscribe(value => {
      console.log('The action is completed.');
    });
  }

}

If I delete the plugin @ngxs/storage-plugin this does not happen.

I leave a gif with the steps of the problem: bug

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Comments: 19 (14 by maintainers)

Commits related to this issue

Most upvoted comments

Both are correct. It’s just that the Select() is trying to retrieve state.featureStage.featureSearch before featureStage is initialized. What you’d want to do is initialize it or give it a default value before theSelect() tries to grab it.

When you set up the state, you can provide the defaults. Something like:

@State({
  name: 'featureStage',
  defaults: {
    featureSearch: {}
  }
})

If you want to select using a lambda and you want to select something that doesn’t exist, you will have to do null checks. until typescript supports optional chaining (https://tc39.github.io/proposal-optional-chaining/) state?.authState?.isLoggedIn then we will have to check the paths all the way down state && state.authState && state.authState.isLoggedIn

As to if you should use the lambda or selector i think the selector is better because you can specify the intent of the query, since they may become quite advanced. It’s why you break a class into multiple methods, you want to be able to reason about a bit of code by just understanding the method name.

  • AuthState.getLoggedInUser instead of state => state.auth != null
  • ModalState.isModalOpen instead of state => state.layout.modal.isOpen

Issue So I don’t think this is a bug, but a problem with how we structure our apps. We can absolutely try to write some good documentation on the matter, and find some good patterns to follow 😃