angularngrxngrx-effectses2022ngrx-signal-store

effect doesn't work with Angular v18 and ngRx


I am using Angular 18 and ngRx.

I wrote a following effect:

import {Injectable} from '@angular/core';
import {Actions, createEffect, ofType} from '@ngrx/effects';
import * as PostsActions from '../actions/actions';
import {catchError, map, mergeMap, of} from 'rxjs';
import {PostService} from '../services/postService';

@Injectable()
export class PostEffects {

  constructor(private actions$: Actions, private postService: PostService) {
    console.log("Constructor");
  }

  getPosts$ = createEffect(() => {
    console.log("Effect Creation")
      return this.actions$.pipe(
        ofType(PostsActions.getPosts),
        mergeMap(() => {
          return this.postService.getPosts().pipe(
            map(posts => posts.posts),
            map((allPosts) => PostsActions.getPostsSuccess({posts: allPosts})),
            catchError((error) =>
              of(PostsActions.getPostsFailure({error: error.message}))
            )
          );
        })
      )
    }
  );

}

and I registered it in this way:

import {ApplicationConfig, isDevMode, provideZoneChangeDetection} from '@angular/core';
import {provideRouter} from '@angular/router';

import {routes} from './app.routes';
import {provideStore} from '@ngrx/store';
import {provideStoreDevtools} from '@ngrx/store-devtools';
import {reducers as postSlice} from './reducers/reducers';
import {provideEffects} from '@ngrx/effects';
import {PostEffects} from './effects/effects';

export const appConfig: ApplicationConfig = {
  providers: [
    provideZoneChangeDetection({eventCoalescing: true}),
    provideRouter(routes),
    provideStore({ postSlice }),
    provideStoreDevtools({maxAge: 25, logOnly: !isDevMode(), autoPause: true}),
    provideEffects([PostEffects])
  ]
};

The problem which I have is that constructor is not called before getPosts$ = createEffect(() => { ... });

It leads to situation where my properties are not initialized.

enter image description here

The statement from: console.log("Constructor"); is not printed.


Solution

  • Basically when you use ES2022 in the target of tsconfig.json you will face this problem. The code defined might be trying to access a property before the constructor is initialized.


    To solve this you can either set the property "useDefineForClassFields": false to solve the problem.


    You can check the two github issues below to find the solution to this problem.

    Github issue - Cannot read properties of undefined (reading 'pipe') #3698

    Github issue - Pre-initialized class fields with a TSConfig target set to ES2022 with useDefineForClassFields set to true will cause runtime issues #3654

    Possible Fixes:


    Initialize the property value inside the constructor:

    export class PostEffects {
      getPosts$;
    
      constructor(private actions$: Actions, private postService: PostService) {
          this.getPosts$ = createEffect(() => {
            return this.actions$.pipe(
              ...
            )
          }
        );
      }
    }
    

    use inject to define actions$ first and then initialize the property:

    export class PostEffects {
      private actions$ = inject(Actions);
      getPosts$ = createEffect(() => {
          return this.actions$.pipe(
            ...
          )
        }
      );
      ...
    }
    

    Inject the actions directly using inject rather than using the property actions$:

    export class PostEffects {
      getPosts$ = createEffect(() => {
          return inject(Actions).pipe(
            ...
          )
        }
      );
      ...
    }