PurpleSmurph
PurpleSmurph

Reputation: 2107

ngrx store selector returning undefined property

To get persistent user information across components I'm trying to implement NgRx store into my solution but have come stuck with getting the data from the store using selectors.

The error that comes back through the console is that user (part of the AuthState) is undefined but when logging in I pass it to the store and can see it when expanding source multiple times.

I've been following the documentation https://www.npmjs.com/package/ngrx-store-localstorage but am now a bit stuck, any suggestions on how to proceed or debug this effectively would be welcome.

Component

this.store.dispatch(addUser({ data: userDetails }));

Action

export const addUser = createAction(
  '[Auth] Add user',
  props<{ data: LoggedUserDetails }>()
);

export const loadUser = createAction(
  '[Auth] Load user'
);

Reducer

export const initialAuthState: AuthState = {
  user: {
    email: '',
    accessToken: '',
    idToken: '',
    name: '',
    id: ''
  },
  authenticated: false
};

// handles the retrieval of the user details
export const authReducer = createReducer(
  initialAuthState,
  on(addUser, (state, { data }) => ({
    ...state,
    user: data,
    authenticated: true
  })),
  on(loadUser, (state) => ({
    ...state,
    user: state.user,
    authenticated: state.authenticated
  }))
);

Selector

export const selectAuthState = (state: AppState) => state.authState;

export const selectUserDetails = createSelector(
  selectAuthState,
  (authState: AuthState) => authState.user);

Module

export function localStorageSyncReducer(
  reducer: ActionReducer<any>
): ActionReducer<any> {
  return localStorageSync({ keys: ['auth'], rehydrate: true })(reducer);
}

const metaReducers: Array<MetaReducer<any, any>> = [localStorageSyncReducer];

// within the imports rather than copying the entire module
StoreModule.forRoot(
  authReducer, { metaReducers }
)

shared component

this.store.pipe(select(selectUserDetails)).subscribe((res: LoggedUserDetails) => {
  // accessing from a shared component this gives the user is undefined error
  this.userDetails = res;
});

Upvotes: 0

Views: 1493

Answers (1)

wlf
wlf

Reputation: 3393

Selectors fire on every state change. Consequently I would guess your selector fires before the user state is initialized - it will fire again when you set the user state.

You can prevent the error by using the ?. operator:

export const selectUserDetails = createSelector(
  selectAuthState,
  (authState: AuthState) => authState?.user;

Also your on in the reducer for loadUser sets the state to the same values it already had, so is redundant.

Upvotes: 2

Related Questions