Reputation: 39
I set up a basic NgRx example in Angular 17.
this is my app.config.ts where I register my reducer in my store
export const appConfig: ApplicationConfig = {
providers: [provideRouter(routes), provideStore({ counterReducer} ), provideStoreDevtools({
maxAge: 25, // Retains last 25 states
logOnly: !isDevMode(), // Restrict extension to log-only mode
autoPause: true, // Pauses recording actions and state changes when the extension window is not open
trace: false, // If set to true, will include stack trace for every dispatched action, so you can see it in trace tab jumping directly to that part of code
traceLimit: 75, // maximum stack trace frames to be stored (in case trace option was provided as true)
}),]
};
app.state.ts
export interface AppState {
value: number;
}
counter.reducer.ts
import { createReducer, on } from '@ngrx/store';
import { increment, decrement, reset } from './counter.actions';
import { AppState } from "./app.state";
export const initialState : AppState = {
value: 0
};
export const counterReducer = createReducer(
initialState,
on(increment, (state) => ({ ...state, value: state.value + 1 })),
on(decrement, (state) => ({ ...state, value: state.value - 1 })),
on(reset, () => initialState)
);
export const selectCounterValue = (state: AppState) => state.value;
my-counter.component.html: This is where I want to show my current value of my State. I am aware that I dont have to subscribe to the count when using async pipe. Just for debugging cases I want to print to console
<button (click)="increment()">Increment</button>
<div>Current Count: {{ (count$ | async ) }}</div>
my-counter.component.ts This where i want to subscribe to my AppState.value. Basically i want to print it out when the state changes.
import {Component, OnDestroy, OnInit} from '@angular/core';
import {Observable, Subscription} from 'rxjs';
import {Store} from "@ngrx/store";
import {increment} from "../store/counter.actions";
import {AppState} from "../store/app.state";
import {state} from "@angular/animations";
import {CommonModule} from "@angular/common";
import {selectCounterValue} from "../store/counter.reducer";
@Component({
selector: 'app-my-counter',
templateUrl: './my-counter.component.html',
imports: [CommonModule],
standalone: true,
})
export class MyCounterComponent implements OnInit, OnDestroy{
count$: Observable<number>;
subscription: Subscription | undefined;
constructor(private store: Store<AppState>) {
this.count$= store.select(selectCounterValue);
}
increment() {
this.store.dispatch(increment());
}
ngOnInit(): void {
this.subscription = this.count$.subscribe(value => {
console.log(value);
});
}
ngOnDestroy(): void {
this.subscription?.unsubscribe();
}
}
counter.reducer.ts:
import { createReducer, on } from '@ngrx/store';
import { increment, decrement, reset } from './counter.actions';
import { AppState } from "./app.state";
export const initialState : AppState = {
value: 0
};
export const counterReducer = createReducer(
initialState,
on(increment, (state) => ({ ...state, value: state.value + 1 })),
on(decrement, (state) => ({ ...state, value: state.value - 1 })),
on(reset, () => initialState)
);
export const selectCounterValue = (state: AppState) => state.value;
As you can see my console.log of my callback value is undefined
However the value in my store is updated correctly when clicking on my increment button
I tried showing my entire state like this:
this.count$= store.select(state => state);
Now I always got a value when clicking but I just want to get the value and not the complete state
What am I missing? I think the undefined has something to do with my select. Basically this is the example of https://ngrx.io/guide/store.
EDIT: I found a workaround but i'm not sure why this is working. Inside my app.config.ts when registering my store i had to give it a name like this:
export const appConfig: ApplicationConfig = {
providers: [provideRouter(routes), provideStore(), provideState({name: "game", reducer: scoreboardReducer}), provideStoreDevtools({
maxAge: 25, // Retains last 25 states
logOnly: !isDevMode(), // Restrict extension to log-only mode
autoPause: true, // Pauses recording actions and state changes when the extension window is not open
trace: false, // If set to true, will include stack trace for every dispatched action, so you can see it in trace tab jumping directly to that part of code
traceLimit: 75, // maximum stack trace frames to be stored (in case trace option was provided as true)
}),]
};
After that i did it like this in my component:
constructor(private store: Store<{ game: AppState}>) {
... }
I checked my store via Redux Devtools and i got values with this approach. When I remove { game: AppState}> it doesn't work anymore and my store is empty.
Upvotes: 0
Views: 349
Reputation: 96
It appears that the issue might be related to how you are selecting the value from the store. To address this, consider utilizing the createSelector
function provided by the NgRx library.
Additionally, if you aim to streamline the process and avoid manually creating selectors for individual pieces of state, you may find createFeature
from NgRx beneficial.
You can take a look at the docs to see how they both works:
https://ngrx.io/api/store/createSelector
https://ngrx.io/api/store/createFeature
Upvotes: 0