Executing an infinite loop can occur when employing an NgRx call to dispatch an action within an Angular router

Story:

Greetings to all! As a newcomer to using the NgRx library, I have encountered an issue in my Angular project where I am utilizing a router guard to prevent unauthorized users from accessing certain parts of the website.

My approach involves calling a REST API to verify the content of a token stored in local storage. Upon receiving a "success" result from the API, I perform "store.dispatch(LoginSuccess)" before returning true to update the login authentication information in the Store.

Problem:

The problem arises when executing the dispatch action. The "LoginSuccess" action seems to trigger an infinite loop even after setting { dispatch: false } in @Effect(). Despite researching and attempting various solutions found through Google, this issue continues to persist within my project. Any assistance or insights from the Stack Overflow community would be greatly appreciated. Thank you in advance.

Snippet of code:

auth.guard.ts

.....
....
canActivate(
    next: ActivatedRouteSnapshot,
    state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {

    const authData: {email: string, token: string} = JSON.parse(
          String(localStorage.getItem('User')));

    const authToken = authData && authData.token;

    // loginServ is a REST API for verify token
    return this.loginServ.VerifyToken(authToken).pipe(
      concatMap((res: AuthResult) => {
        if (res.result === 'success') {

          this.store.dispatch(LoginSuccess({
            isAuthenticated: true,
            email: authData && authData.email,
            token: authToken,
            result: 'success',
            message: 'success'
          }));  // <-- execute action infinite.

          return of(true);
        } else {

          this.store.dispatch(LoginFailed({
            isAuthenticated: false,
            email: authData && authData.email,
            token: authToken,
            result: 'failed',
            message: 'token illegal'
          }));

          localStorage.removeItem('User');
          this.router.navigate(['']);
          return of(false);
        }
      })
    );
...
...

action.ts:

...
export const LoginSuccess = createAction(
  '[auth] Login Success',
  props<{
    isAuthenticated: boolean,
    email: string,
    token: string,
    result: string,
    message: string,
  }>()
);
....
...

reducer.ts

....
....
export const loginReducer = createReducer(
  defaultState,

  on(Login),

  on(LoginSuccess, (state, userInfo) => {
    console.log(state);     // <-- for debug.
    console.log(userInfo);  // <-- for debug.
    return {
      ...state,
      ...userInfo
    };
  }),

  on(LoginFailed, (state, result) => {
    return {
      ...state,
      ...result
    };
  }),
....
...

effect.ts

....
...
loginSuccess = createEffect(() => this.actions$
  .pipe(
    ofType(LoginSuccess),
    map((user: any) => {

      console.log(user);
      const beStoreInfo = {
        email: user.email,
        token: user.token,
      };

      localStorage.setItem(
        'User',
        JSON.stringify(beStoreInfo));
        return {
          type: '[auth] Login Success',
          isAuthenticated: true,
          email: user.email,
          token: user.token,
          result: user.result,
          message: user.message
        };
    }),
  ), {dispatch: false});
....
...

service_overview-routing.module.ts

....
// Using lazy loading module
const routes: Routes = [
  {
    path: '',
    canActivate: [AuthGuard],
    component: ServiceOverviewComponent},
];

@NgModule({
  imports: [RouterModule.forChild(routes)],
  exports: [RouterModule]
})
export class ServiceOverviewRoutingModule { }
....

app-routing.module.ts

Implementing lazy loading modules.

....
...
const routes: Routes = [
  { path: 'login', loadChildren: () => import('./login/login.module')
    .then(m => m.LoginModule)},
  { path: 'register', loadChildren: () => import('./register/register.module')
    .then(m => m.RegisterModule)},
  { path: 'service_overview', loadChildren: () => import('./service-overview/service-overview.module')
    .then(m => m.ServiceOverviewModule)},
  { path: '', redirectTo: 'login', pathMatch: 'full' }
];
....

Environment:

Angular CLI: 8.0.3

NgRx Store/Effect: 8.0.1

OS: Ubuntu 18.04

..Thanks

Answer №1

Your success login trigger causes another success login action, creating an endless loop.

loginSuccess = createEffect(() => this.actions$
  .pipe(
    ofType(LoginSuccess),
    map((user: any) => {

      console.log(user);
      const beStoreInfo = {
        email: user.email,
        token: user.token,
      };

      localStorage.setItem(
        'User',
        JSON.stringify(beStoreInfo));
        // Please remove the following ⬇
        return {
          type: '[auth] Login Success',
          isAuthenticated: true,
          email: user.email,
          token: user.token,
          result: user.result,
          message: user.message
        };
        // End of removal ⬆
    }),
  ), {dispatch: false});

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

How can we retrieve URL parameters with the help of ActivatedRoute in Angular?

My current route configuration is as follows: { path: "events", component: SkeletonComponent, children: [ { path: ':id', component: ViewEventComponent } ] } The constructor of my component looks ...

Avoid retrieving information from Firestore

Hey everyone, I'm struggling to figure out why I can't retrieve data from Firestore. Even after carefully reading the documentation and double-checking the path, I still can't seem to make it work. I'm working with Ionic framework. getC ...

Challenges Encountered When Running Angular 5 Karma Tests with Imports

Recently, I made the transition from Angular 2 to 5 for a project and encountered an issue where test cases related to compiling views started failing. Tests that were previously successful are now not working as expected. In order to troubleshoot the pro ...

Navigating an immutable list to make updates to its values

Within this list, I have an unalterable group of objects. My task is to change the value of the 'isReq' property to false for all objects except the one with the id 2. [ { 'id': 1, 'name': 'Ram', 'D ...

React components need to refresh after fetching data from an API

I am currently working on a React application using TypeScript and integrating JSONPlaceholder for simulating API calls. I have successfully set up everything I need, but I am encountering an issue with re-rendering components that display response data fr ...

Can you explain the functionality of this Observable in the context of this Angular 2 example?

I'm not too familiar with JavaScript/TypeScript and I have a question about how this code snippet works: onGet() { this.serverService.getServers() .subscribe( (servers: any[]) => this.servers = servers, // an array of anythin ...

Do not display large numbers within an HTML card

I have https://i.sstatic.net/DkowD.png this card here and displaying dynamic data inside it. The number is quite large, so I would like it to appear as 0.600000+. If a user hovers over the number, a tooltip should display the full number. How can I achieve ...

What situations call for the use of 'import * as' in TypeScript?

Attempting to construct a cognitive framework for understanding the functionality of import * as Blah. Take, for instance: import * as StackTrace from 'stacktrace-js'; How does this operation function and in what scenarios should we utilize imp ...

Cannot locate module in Jest configuration

I'm struggling to understand config files and encountering issues while attempting to run jest unit tests: Cannot locate module '@/app/utils/regex' from 'src/_components/DriverSearchForm.tsx' Here's my jest configuration: ...

Launching a Material UI Modal nested within a parent component

I have a table displaying various teams. Each row in the table has a menu option that, when clicked, should open either a modal or a dialog box. I want to keep the table, menu functionality, and modals as separate components for better organization. Here&a ...

Error with dateAdapter targeting a particular language

Currently facing an issue with the Angular material date picker when using a specific language - ka. Here is an example of the problem: constructor( private dateAdapter: DateAdapter<Date>, public noticesService:NoticesService, public d ...

Creating a button that displays the current day with Angular

I'm in the process of developing a timetable app that features buttons for the previous day, current day, and next day. How can I implement a button to specifically show the current day? HTML File <button type="button" (click)="previousDay()" ...

Creating dirty or touched input programmatically in Angular2 can be achieved by using the `.markAsT

Looking to integrate a JQuery plugin with Angular2? Essentially, this plugin generates a duplicate of specific HTML content containing inputs. As data is modified, Angular2 automatically re-renders the content along with any errors. However, the challenge ...

Where should I place the function URL and the path to credentials.json when attempting to call a Google Cloud Function with CloudFunctionsServiceClient?

I found this code snippet on here: /** * TODO(developer): Uncomment these variables before running the sample. */ /** * Required. The name of the function to be called. */ // const name = 'abc123' /** * Required. Input to ...

Exploring i18nNext integration with antd table in React

Presently, this is the UI https://i.stack.imgur.com/CMvle.png App.tsx import "./styles.css"; import MyTable from "./MyTable"; export default function App() { const data = [ { key: "1", date: "2022-01- ...

Checking for GitHub API connectivity issues with GitHub can be done by verifying whether the GitHub API is

Is there a way to check from the GitHub API if it is unable to connect to GitHub or if the internet is not connected? When initializing the API like this: GitHubApi = require("github"); github = new GitHubApi({ version: "3.0.0" ...

Steps for setting up ngx-bootstrap version 7.0.0 in Angular 11

I am currently using ngx-bootstrap 5.x.x in my Angular 8 project. However, after migrating my Angular version from 8 to 11, I have encountered issues with the dropdown component. It does not open on the first click. I have been advised to use version 7.0.0 ...

Can NgZone be utilized within a shared service instance?

I am currently working on creating an injectable singleton service for my application that will provide all components with information about the window width and height, as well as notify them when the page is scrolled or resized. Below is the code snipp ...

Creating a list with axios in the ngOnInit lifecycle hook

I have a feature where I need to populate objects fetched from the backend. ngOnInit() { this.source.load(this.myService.findAll()); } Within myService, I am using Axios to retrieve data from the backend. I can confirm that the data is success ...

Exploring an array in Angular 2 using TypeScript

Just starting out with typescript and angular2 and working through some issues. I have a form that needs to display results from an array of changing items, so I don't know the exact index of each result. Here is my scenario: In my form.html file: ...