I want that when an API call returns a user the state is updated and the home.component.html shows the new value. I am also using ngrx devtools and I can see the state change, but the html does not update. This is what I have.
home.component.ts
export class HomeComponent implements OnInit {
meUser$ = this.store.select(selectMeUser);
constructor(private store: Store<AppState>) { }
ngOnInit(): void {
this.store.dispatch(loadMeUser());
}
home.component.html
<p *ngIf="meUser$ | async as me">
Hi, {{ me.firstname }}
</p>
user.actions.ts
export const loadMeUser = createAction('[User] Load Me User');
export const loadMeUserSuccess = createAction(
'[User] Load Me User Success',
props<{ user: User }>()
);
export const loadMeUserFailure = createAction(
'[User] Load Me User Failure',
props<{ error: string }>()
);
user.reducers.ts
export interface UserState {
meUser: User | null;
status: string;
error: string;
}
export const initialstate: UserState = {
meUser: null,
status: 'pending',
error: ''
}
export const userReducer = createReducer(
initialstate,
on(loadMeUser, (state) => ({ ...state, status: 'loading' as StatusType })),
on(loadMeUserSuccess, (state, { user }) => ({
...state,
meUser: user,
error: '',
status: 'success'
})),
on(loadMeUserFailure, (state, { error }) => ({
...state,
error: error,
status: 'error'
}))
)
user.selectors.ts
export const selectUser = (state: AppState) => state.user;
export const selectMeUser = createSelector(
selectUser,
(state: UserState) => state?.meUser
)
user.effects.ts
@Injectable()
export class UserEffects {
constructor(
private actions$: Actions,
private store: Store<AppState>,
private userService: UserService
) {}
loadMeUser$ = createEffect(() =>
this.actions$.pipe(
ofType(loadMeUser),
exhaustMap(() => {
return this.userService.getMeUser().pipe(
map((data) => {
return loadMeUserSuccess({ user: data });
}),
catchError((error) => of(loadMeUserFailure({ error: error })))
)
})
)
)
}
In the devtools I can see the state result is success and if I add a console.log inside the effect the data is correct, if I change it to meUser$ = this.store.select(selectMeUser).subscribe((data) => console.log(data))
I get undefinied.
app.module.ts
@NgModule({
declarations: [
...
],
imports: [
...,
StoreModule.forRoot({ users: userReducer }),
StoreDevtoolsModule.instrument({
maxAge: 25,
trace: true,
traceLimit: 25
}),
EffectsModule.forRoot([UserEffects])
],
providers: [
...
],
bootstrap: [AppComponent, ...]
})
export class AppModule { }
What am I missing?
There's a typo
export const selectUser = (state: AppState) => state.user;
user
should be userS
:
export const selectUser = (state: AppState) => state.users;
The reason is that it's configured as users
while configuring the store:
StoreModule.forRoot({ users: userReducer }),
To prevent this, you can make use of feature keys https://ngrx.io/guide/store/selectors#selecting-feature-states or build your state with feature creators https://ngrx.io/guide/store/feature-creators