Understanding NGRX Select: Exploring inner object selections

I am relatively new to NGRX and I am attempting to set up a basic NGRX example. In my project, I have an interface named LoginState, which serves as the Object in my Store. Inside the LoginState, there is a user object of type IUser. Whenever I try to select this object in my component, I do not receive the IUser when the user object undergoes changes (although my effects appear to be functioning correctly, as subscribing directly to the store returns all state changes).

Here is how I have configured everything:

app.module.ts

imports: [
    ...
    StoreModule.forRoot({loginState: loginReducer})
]

login.actions.ts

export const userLoginRequest = createAction('[Login] user login request');
export const userLoginFacebookSuccess = createAction('[Login] user login facebook success', props<{socialUser: SocialUser}>());
export const userLoginSuccess = createAction('[Login] user login success', props<{user: IUser}>());

login.reducer.ts

export interface LoginState {
    request: boolean;
    socialUser?: SocialUser;
    user?: IUser;
}

export const initialState: LoginState = {
    request: false
};

const reducer = createReducer(initialState,
    on(userLoginRequest, (state, action) => ({
        ...state,
        request: true
     })),
    on(userLoginFacebookSuccess, (state, action) => ({
        ...state,
        socialUser: action.socialUser
    })),
    on(userLoginSuccess, (state, action) => ({
        ...state,
        user: action.user
    }))
);

export function loginReducer(state: LoginState, action: Action): LoginState {
    return reducer(state, action);
}

login.effects.ts

import { userLoginRequest, userLoginSuccess, userLoginFacebookSuccess } from './login.action';

@Injectable()
export class LoginEffects {

loginFacebook$ = createEffect(() => this.actions$.pipe(
    ofType(userLoginRequest),
    switchMap(() =>
        this.authService.loginFacebook()
            .pipe(
                 map((socialUser) => {
                     this.tokenService.saveToken(socialUser.authToken);
                     return userLoginFacebookSuccess({socialUser});
                 })
            )
        )
     )
 );

loadUser$ = createEffect(() => this.actions$.pipe(
    ofType(userLoginFacebookSuccess),
    switchMap((action) =>
        this.userService.getUser(action.socialUser)
            .pipe(
                map((user) => userLoginSuccess({user}))
            )
        )
    )
);


constructor(
  private actions$: Actions,
  private authService: AuthWrapperService,
  private tokenService: TokenStorageService,
  private userService: UserService
) {}

}

Login.component.ts

export class LoginComponent implements OnInit {

localUser: IUser;

constructor(
          private router: Router,
          private store: Store<LoginState>
){}

signInWithFB(): void {
    this.store.dispatch(userLoginRequest());
}

ngOnInit() {
    this.store.select(selectUser).subscribe( (user) => {
    console.log(JSON.stringify(user, undefined, 2));
}
);
}
}

const getLoginState = (state: LoginState) => state;

export const selectUser = createSelector(
  getLoginState,
  (state: LoginState) => state.user
);

Thank you for assisting me with this!

Best regards,

Tho

Answer №1

Your current code is solid, but one important thing to note is that ngrx immediately selects data, even if it's not yet available.

So, the first time this.store.select(selectUser) runs before the user has been loaded, it will return undefined as expected.

Once userLoginSuccess is processed and there's a user in the store, the selector will emit the correct user value.

If you always expect to receive a user, you can filter out any unwanted emits from the stream. Also, remember that your state resides under loginState, so make sure to use createFeatureSelector.


const loginStateFeature = createFeatureSelector<LoginState>('loginState');

export const selectUser = createSelector(
  loginStateFeature,
  (state: LoginState) => state.user
);


ngOnInit() {
  this.store.select(selectUser).pipe(
    filter(user => !!user), // discard falsy emits.
  ).subscribe((user) => {
    console.log(JSON.stringify(user, undefined, 2));
  });
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Attempting to create an interactive stepper within a material dialog using reactive forms for a versatile "smart" helper feature

Currently, I am working on developing an assistant that can display different steps based on the data it receives. This flexibility will allow me to use the assistant for various user types, each requiring a unique set of steps. For example, registration m ...

How to efficiently reduce an array in ES6 without encountering any TypeScript errors

Is there a way to efficiently remove the first array element without modifying the original array (immutable)? I have this code snippet: function getArray(): number[] { return [1, 2, 3, 4, 5]; } function getAnother(): number[] { const [first, ...rest ...

The form group unit test encountered an issue with reading the prop value, as it

While attempting to create a unit test for my component, I encountered an issue where updating a value in a FormGroup within a FormArray led to the following error during the test: Cannot Read Prop value of undefined Although the code compiles without er ...

The Fixed Navbar is causing sections to be slightly off from their intended positions

Utilizing a bootstrap navigation menu on my website with a fixed position. When clicking a menu item, it takes me to the designated section but slightly above the desired position. How can I ensure that it goes to the exact position upon clicking the men ...

Unable to resolve all parameters for the RouterUtilities class

My goal is to develop a RouterUtilities class that extends Angular's Router. Despite the app running and compiling smoothly, when I run ng build --prod, it throws an error message like this: ERROR in : Can't resolve all parameters for RouterUtil ...

Automate your Excel tasks with Office Scripts: Calculate the total of values in a column depending on the criteria in another column

As a newcomer to TypeScript, I have set a goal for today - to calculate the total sum of cell values in one column of an Excel file based on values from another column. In my Excel spreadsheet, the calendar weeks are listed in column U and their correspon ...

Managing the accumulation of response chunks in a streaming request with Axios

I have a proxy server that needs to make a request to an external API server to synthesize a voice from some text. According to the API docs, I will first receive a response with headers and then stream binary data, as the response body contains 'Tran ...

Testing an angular function that requires multiple arguments in its constructor

My goal is to conduct unit tests on functions within a component. The constructor for this component requires four arguments. Initially, I attempted to simply set the arguments as (new AClass, new BClass, new CClass, new DClass); however, some of these cla ...

Error message: The issue arises when trying to incorporate jQuery-UI into an Angular 4 application due to the undefined status of

I'm currently working on integrating jQuery-UI into an application to utilize some sliders. I've successfully installed the typings for jQuery by running: npm install @types/jquery --save And it seems I've also installed the jQuery-UI typi ...

Having trouble minifying the bundle on the most recent version of Angular 2 with JSPM

Encountering an issue with minifying the bundle in the latest Angular 2 version using jspm... Here's the error message: events.js:160 throw er; // Unhandled 'error' event ^ GulpUglifyError: unable to minify JavaScript at cr ...

What is the best way to handle an OR scenario in Playwright?

The Playwright documentation explains that a comma-separated list of CSS selectors will match all elements that can be selected by one of the selectors in that list. However, when I try to implement this, it doesn't seem to work as expected. For exam ...

Discovering the underlying object in Angular without needing an event while using *ngFor

Is there a way to retrieve the object associated with a DOM element based on the element's id without using an event trigger? For example, if we have: arr1 = [{a:1, b:2}, {a:5, b:10}, {a:20, b:50}]; <li *ngFor="let obj of arr1; let indexObj=inde ...

Troubleshooting font family problems in Angular Editor's rich text kolkov library

When using Angular Editor for rich text editing and styling, there seems to be an issue with changing the font family. The option to select a different font from the list is always disabled, with the selected defaultFontName being the only available choice ...

What is the best way to distinguish elements in the same HTML using Angular Material?

Currently, I am working on a project using Angular material, where I have a component.html file that defines a form and a table with data. In the first image of my project, you can see the tab title, a form to add new records, and the table displaying exi ...

Generate personalized fields for Interface based on the Response Received

In my SPFX web part, the web service I am calling has properties that start with numbers: 30DaysTotal, 60DaysTotal, and 90DaysTotal. To handle this, I have defined an Interface as follows: export interface ISummary { Id : number; "30DaysGrand ...

Is it possible to configure Angular 6 without relying on node.js/npm?

Is there a way to set up and run an Angular application without relying on the Angular CLI or needing Node.js and npm commands in the terminal? I would greatly appreciate any suggestions regarding this matter. ...

Is it possible to maintain type inference in union types when utilizing type guards within a function?

Imagine I am working with three unique types (and one union type) type Alpha = { type: 'a' title: string description: string } type Beta = { type: 'b' title: string } type Charlie = { type: 'c' de ...

Encountering Issues with Deploying Angular/Express Application on Heroku: Frontend Unable to Access API Endpoint

During the development phase, everything runs smoothly with localhost:4200 for the front-end and localhost:8080 for the back-end. After deployment, the front-end is displayed without any issues. However, there is a problem with fetching data from the API ...

Using Angular 2 along with RxJS to transform an array using Rx Observables

I am working with an array of numbers, specifically [1, 2, 3], and utilizing an HTTP service that includes a function to load data objects based on a given number: function get(id: number): Observable<object> Can anyone help me figure out how to map ...

Utilize a type from a different global module that shares a common submodule

I am currently working on a project that requires me to export typings for two separate Java libraries, each with its own unique class hierarchy. libName `-namespace1 `-Class1 // full class Name: (libName.namespace1.Class1) -C ...