mesx
mesx

Reputation: 1165

ngrx side effect: wait for store data or cancel

please let me provide some background info:

In my application, I have a list of "features". A feature has an ID and a state (activated: boolean). I manage those features inside a ngrx store. It is basically an array of these features. These features must be fetched from the server and can change at runtime. The store is updated after app start and on other events. Angular components observer these features. Most of the filter features by state (so most of them are only interested in active features). In order to activate a feature, a component needs to dispatch an ACTIVATE_FEATURE action, or a specific URL must be active. Some features register on a URL path, while others on URL parameters.

I already observe the URL using the ngrx router-store. I am also able to determine which feature must be activated according to the URL.

Now to the question:

I was thinking to activate a feature using a ngrx side effect on the router-state like so:

@Effect()
private routerEffect = this.actions$.ofType(ROUTER_NAVIGATION)

Then look at the payload and dispatch an ACTIVATE_FEATURE action in order for my feature reducer to set a given feature to active: true. But here is the thing: What if the stores do not yet contain the feature yet? Or never? If the user navigates to a specific URL then this is always fine but if the store gets updated with the feature who matches the URL, then the feature should be activated once it is loaded. It must also be possible to change the URL even if the store is still loading. A feature must get deactivated once the URL does not match its criteria anymore.

I was experimenting with something like this but i simply don't know how to approach this...

@Effect()
private routerEffect = this.actions$.ofType(ROUTER_NAVIGATION)
    .map((nav: RouterNavigationAction<fromRouter.State>) => nav.routerState.data.featureIdToActivate)
    .withLatestFrom(this.store.select(fromFeatures.selectAllFeatures)) // this store holds an array of features
    // now what?? Is this even correct?
    // i would need to dispatch something like {type: 'ACTIVATE_FEATURE', payload: featureIdToActivate}

Even if i would manage to wait for the store to contain the desired feature, how to cancel the wait when the URL changes??

Any suggestions are very much appreciated! Thank you!

Upvotes: 0

Views: 1838

Answers (1)

Ophir Bushinsky
Ophir Bushinsky

Reputation: 1429

will something like the following help you?

@Effect()
router.events
    .pipe(
        filter(event => event instanceof NavigationEnd) // or whatever you wish
        switchMap((event) => {
            return store
                .pipe(
                    map((state) => {
                        const featureId = 
                            getFeatureIdToActivate(event);
                        return state.features.find(f => f.id === featureId);
                    }),
                    filter(feature => feature != null),
                    map(feature => new ActivateFeatureAction(feature))
                );
        })
    );

Here we are listening to the router for navigation end events, or any kind of event you would like.

For every event we switchMap to the store (meaning that any new event would cancel the old ones).

Inside the switchMap you can use your logic to decide which feature to activate, since you have both the event, and the state.

Then you only dispatch ActivateFeatureAction if the correct feature is found. (we are listening also to the store, so if a feature is later added we will catch it).

Upvotes: 1

Related Questions