NgRx router-store emits route parameter changes when navigating away from current page

1.8k Views Asked by At

I have an Angular component that needs to respond to route parameter changes that apply to the current page. For example, maybe I have the route page/:id, and if the :id changes, I want to respond by reloading the component's content.

Using the ActivatedRoute's paramMap, this is easy. While I'm on the page/:id route, I can change the :id in the URL and the application responds appropriately. When I navigate away from the page/:id route, the component is destroyed, and no parameter changes are emitted.

However, if I try the same approach with @ngrx/router-store, more route param changes are emitted than I actually want. I am getting the route params from the route that is being navigated to. This seems to happen because the router-store emits before my component gets destroyed.

I need a way to say "I only want the route params for this page, and as soon as this page is navigated away from, I need you to stop responding to route param changes." I thought this would be as easy as implementing a takeUntil(unsubscriber$) which completes in my ngOnDestroy(). However, that doesn't work because the next params emission happens before the ngOnDestroy() gets called.

I imagine this behavior is by design, but I'm not sure how to overcome the problem I'm facing.

I've worked up a Stackblitz that demonstrates the issue.

What's the most elegant way to handle this situation using @ngrx/router-store?

Working example using ActivatedRoute

export class MyComponent implements OnInit, OnDestroy {
  unsubscriber$ = new Subject<void>();

  constructor(public route: ActivatedRoute) {}

  ngOnInit() {
    this.route.paramMap
      .pipe(takeUntil(this.unsubscriber$))
      .subscribe(params => {
        // reload data
      });
  }

  ngOnDestroy() {
    this.unsubscriber$.next();
    this.unsubscriber$.complete();
  }
}

Non-working example using router-store

export class MyComponent implements OnInit, OnDestroy {
  unsubscriber$ = new Subject<void>();

  constructor(public store: Store<State>) {}

  ngOnInit() {
    this.store
      .pipe(select(selectRouteParams), takeUntil(this.unsubscriber$))
      .subscribe(params => {
        // reload data
        // this code is being run even when this component is being navigated away from
      });
  }

  ngOnDestroy() {
    this.unsubscriber$.next();
    this.unsubscriber$.complete();
  }
}
1

There are 1 best solutions below

1
On

Change your navigation action timing to PostActivation via the configuration object passed in to StoreRouterConnectingModule.forRoot:

@NgModule({
  imports: [
    CommonModule,
    ...
    StoreRouterConnectingModule.forRoot({
      navigationActionTiming: NavigationActionTiming.PostActivation,
    }),
  ],
})