最新消息:Welcome to the puzzle paradise for programmers! Here, a well-designed puzzle awaits you. From code logic puzzles to algorithmic challenges, each level is closely centered on the programmer's expertise and skills. Whether you're a novice programmer or an experienced tech guru, you'll find your own challenges on this site. In the process of solving puzzles, you can not only exercise your thinking skills, but also deepen your understanding and application of programming knowledge. Come to start this puzzle journey full of wisdom and challenges, with many programmers to compete with each other and show your programming wisdom! Translated with DeepL.com (free version)

javascript - NgRx store - Selectors are not working for a root global store - Stack Overflow

matteradmin6PV0评论

I am very new to ngrx, and just trying to get my head around it, and get something working.

I have added ngrx (version 8.3) to my application.

I wish to have a few things in a root state (if possible), and then have separate states for each of my features. I started at the root state, but the selector I have is just never notified.

I have the following actions...

    // actions
    import { createAction, union } from '@ngrx/store';
    import { SecurityTokensState } from './app.reducer';

    export const setUrl = createAction(
      '[App url] ',
      (payload: string) => ({ payload })
    );

    export const setTokens = createAction(
      '[App setSecurityTokens] ',
      (payload: SecurityTokensState) => ({ payload })
    );


    export const actions = union({
      setUrl,
      setTokens
    });

    export type ActionsUnion = typeof actions;

And the following reducers..

    import * as rootActions from './app.actions';
    import { createReducer, on } from '@ngrx/store';

    /** Top level state */
    export interface State {
      /** State to do with Auth */
      tokens: SecurityTokensState;

      /** General / root app state (eg configuration) */
      app: AppState
    }

    /** App wide general state */
    export interface AppState {
      url: string;
      //extraLogging: boolean;
      //offlineExpiry: number; 
      //offlineTime: number;
    }

    /** Security token state */
    export interface SecurityTokensState {
      token: string,
      refreshToken: string;
    }

    const initialState: State = { tokens: undefined, app: { url: ""}  };

    export function rootReducer(state: State, action: rootActions.ActionsUnion): State {
      return reducer(state, action);
    }

    const reducer = createReducer(
      initialState,
      on(rootActions.setTokens,
        (state, { payload }) => ({ ...state, tokens: payload })
      ),
      on(rootActions.setUrl,
        (state, { payload }) => ({ ...state, app: updateUrl(state, payload)}))
    )

    /**  Helper to update the nested url */
    const updateUrl = (state: State, payload: string): AppState => {      
      const updatedApp = { ...state.app };
      updatedApp.url = payload;
      return updatedApp;
    }

And I created the following selector...

import { createFeatureSelector, createSelector } from "@ngrx/store";
import { AppState } from './app.reducer';

const getAppState = createFeatureSelector<AppState>('app');

export const getUrl = createSelector(
  getAppState,
  state => state.url
  );

In app.module, I have the following...

StoreModule.forRoot(rootReducer),

Now, in a ponent, I have

   import * as rootSelectors from '../state/app.selectors';
    ....

    public onUrlBlur(ev : any): void {   
       let val = ev.target.value;
       this.store.dispatch(rootActions.setUrl(val));   
      }

And I have the code to subscribe to the update

 this.subs.sink = 
    this.store.pipe(select(rootSelectors.getUrl)).subscribe(url => {
    this.urlEntered = url        
  });

Lastly, just as a placeholder, in one of my feature modules, I have added...

   StoreModule.forFeature('myfeature1', {})

I see the blur function being called, and in the redux dev tools I see

But for the state, all I see is

And the observable this.store.pipe(select(rootSelectors.getUrl)).subscribe(url => {` never fires

So my root state does just not seem to be in there, and I really can't see what I have done wrong.

Where have I messed up here?

Update

Have added a very similar example (with the same problem) here

When running, and go to the console, can see the following...

selector.ts:610 The feature name "appRoot" does not exist in the state, therefore createFeatureSelector cannot access it. Be sure it is imported in a loaded module using StoreModule.forRoot('appRoot', ...) or StoreModule.forFeature('appRoot', ...). If the

What I don't understand if how to use the StoreModule.forRoot(rootReducer ),

In the error, it suggests using a string... eg StoreModule.forRoot('app', rootReducer ), but this gives a syntax error.

If I do the following:

 StoreModule.forRoot({appRoot: rootReducer} ),

I get a nested state:

But just passing the reducer:

StoreModule.forRoot(rootReducer ),

I get no state:

All the examples I see use just feature states, but I have a number of settings that are just application wide, and not in a feature module.

Also, as this state is not in a feature module, I am not sure if I should be using the createFeatureSelector:

const getAppState = createFeatureSelector<AppState>('appRoot');

I am very new to ngrx, and just trying to get my head around it, and get something working.

I have added ngrx (version 8.3) to my application.

I wish to have a few things in a root state (if possible), and then have separate states for each of my features. I started at the root state, but the selector I have is just never notified.

I have the following actions...

    // actions
    import { createAction, union } from '@ngrx/store';
    import { SecurityTokensState } from './app.reducer';

    export const setUrl = createAction(
      '[App url] ',
      (payload: string) => ({ payload })
    );

    export const setTokens = createAction(
      '[App setSecurityTokens] ',
      (payload: SecurityTokensState) => ({ payload })
    );


    export const actions = union({
      setUrl,
      setTokens
    });

    export type ActionsUnion = typeof actions;

And the following reducers..

    import * as rootActions from './app.actions';
    import { createReducer, on } from '@ngrx/store';

    /** Top level state */
    export interface State {
      /** State to do with Auth */
      tokens: SecurityTokensState;

      /** General / root app state (eg configuration) */
      app: AppState
    }

    /** App wide general state */
    export interface AppState {
      url: string;
      //extraLogging: boolean;
      //offlineExpiry: number; 
      //offlineTime: number;
    }

    /** Security token state */
    export interface SecurityTokensState {
      token: string,
      refreshToken: string;
    }

    const initialState: State = { tokens: undefined, app: { url: ""}  };

    export function rootReducer(state: State, action: rootActions.ActionsUnion): State {
      return reducer(state, action);
    }

    const reducer = createReducer(
      initialState,
      on(rootActions.setTokens,
        (state, { payload }) => ({ ...state, tokens: payload })
      ),
      on(rootActions.setUrl,
        (state, { payload }) => ({ ...state, app: updateUrl(state, payload)}))
    )

    /**  Helper to update the nested url */
    const updateUrl = (state: State, payload: string): AppState => {      
      const updatedApp = { ...state.app };
      updatedApp.url = payload;
      return updatedApp;
    }

And I created the following selector...

import { createFeatureSelector, createSelector } from "@ngrx/store";
import { AppState } from './app.reducer';

const getAppState = createFeatureSelector<AppState>('app');

export const getUrl = createSelector(
  getAppState,
  state => state.url
  );

In app.module, I have the following...

StoreModule.forRoot(rootReducer),

Now, in a ponent, I have

   import * as rootSelectors from '../state/app.selectors';
    ....

    public onUrlBlur(ev : any): void {   
       let val = ev.target.value;
       this.store.dispatch(rootActions.setUrl(val));   
      }

And I have the code to subscribe to the update

 this.subs.sink = 
    this.store.pipe(select(rootSelectors.getUrl)).subscribe(url => {
    this.urlEntered = url        
  });

Lastly, just as a placeholder, in one of my feature modules, I have added...

   StoreModule.forFeature('myfeature1', {})

I see the blur function being called, and in the redux dev tools I see

But for the state, all I see is

And the observable this.store.pipe(select(rootSelectors.getUrl)).subscribe(url => {` never fires

So my root state does just not seem to be in there, and I really can't see what I have done wrong.

Where have I messed up here?

Update

Have added a very similar example (with the same problem) here

When running, and go to the console, can see the following...

selector.ts:610 The feature name "appRoot" does not exist in the state, therefore createFeatureSelector cannot access it. Be sure it is imported in a loaded module using StoreModule.forRoot('appRoot', ...) or StoreModule.forFeature('appRoot', ...). If the

What I don't understand if how to use the StoreModule.forRoot(rootReducer ),

In the error, it suggests using a string... eg StoreModule.forRoot('app', rootReducer ), but this gives a syntax error.

If I do the following:

 StoreModule.forRoot({appRoot: rootReducer} ),

I get a nested state:

But just passing the reducer:

StoreModule.forRoot(rootReducer ),

I get no state:

All the examples I see use just feature states, but I have a number of settings that are just application wide, and not in a feature module.

Also, as this state is not in a feature module, I am not sure if I should be using the createFeatureSelector:

const getAppState = createFeatureSelector<AppState>('appRoot');
Share Improve this question edited Dec 5, 2020 at 11:09 halfer 20.4k19 gold badges109 silver badges202 bronze badges asked Oct 7, 2019 at 3:31 petercpeterc 7,88314 gold badges83 silver badges164 bronze badges 3
  • Can you share your code on stackblitz or github ? – Tony Commented Oct 7, 2019 at 6:26
  • I have added an example here – peterc Commented Oct 7, 2019 at 8:11
  • In terms of having the url (and params and queryParams) as part of the app state, there's @ngrx/router-store for that. Why reinvent the wheel? I gave an answer to a similar question here – Andrew Allen Commented Oct 7, 2019 at 12:57
Add a ment  | 

2 Answers 2

Reset to default 3

The StoreModule.forRoot() function expects a ActionReducerMap, not a reducer function.

See the docs for more info.

To resolve the nested state problem, in your case this would look as follows:

StoreModule.forRoot({
  tokens: tokensReducer,
  appRoot: appRootReducer
})

or you can do:

StoreModule.forRoot({}),
StoreModule.forFeate('appRoot', appRootReducer)

I guess your reducer is not configured properly, that's why the fired action is not changing the state. And as there is no change in the state this.store.pipe(select(rootSelectors.getUrl)).subscribe(url => {}), this subscription is never fired.

Post a comment

comment list (0)

  1. No comments so far