Search code examples
angularreduxngrx-store

NgRx redirect to dashboard on loginSuccess Action


I'm learning NgRx and I have a basic question. I've been looking at sample projects and other answers and they are all from 2017 where the architecture was a bit different.

I'm implementing a login feature, everything so far is working fine, actions are being called, rest Api request been made, etc. My only question is: where do I redirect the user to the dashboard?

Actions:

export const loginAction = createAction(
    '[Login Page] User Login',
    props<{ login: string; password: string }>()
);

export const loginSuccessAction = createAction(
    '[Auth] Login Complete',
    props<{user: UserModel}>()
);

export const loginFailAction = createAction('[Auth] Login Fail', props<{error: any}>());

Reducers:

export interface AuthState{
    isLoggedIn: boolean;
    user: UserModel | null;
}
export const initialState: AuthState = {
    isLoggedIn: false,
    user: null
};

const _authReducer = createReducer(
    initialState,
    on(loginAction, (state) => ({...state, isLoggedIn: false, user: null})),
    on(loginSuccessAction, (state, {user}) => ({...state, isLoggedIn: true, user: user})),
    on(loginFailAction, (state) => ({ isLoggedIn: false, user: null }))
);

// tslint:disable-next-line:typedef
export function authReducer(state, action) {
    return _authReducer(state, action);
}

Effects:

@Injectable()
export class AuthEffects {
    constructor(
        private actions$: Actions,
        private authService: AuthService
    ) {}

    @Effect()
    login$ = createEffect(() =>
        this.actions$.pipe(
            ofType(loginAction),
            exhaustMap(action =>
                this.authService.login(action.login, action.password).pipe(
                    map(user => loginSuccessAction({user: user.redmine_user})),
                    catchError(error => of(loginFailAction({ error })))
                )
            )
        )
    );
}

In my component:

ngOnInit(): void
{
    this.loginForm = this._formBuilder.group({
        login   : ['', Validators.required],
        password: ['', Validators.required]
    });
}

onLogin(): void{
    this._store.dispatch(loginAction(this.loginForm.value));

}

ngOnDestroy(): void {

}

As I said, actions are being triggered successfully. I can see the loginSuccessAction being called in the Redux Dev Tools. I've seen people doing the redirect inside the effect, but is that the right place to do it? If not, where should I do? Is there something like listen to actions?


Solution

  • If I understand correctly, you're asking where to perform redirection after a user logs in, but not in the technical sense of how to do it but in an architectural sense of where is the right place to do it.

    Let's go over the options you have:

    1. Redirect in the effect - This is your first option, redirecting in the effect before or after dispatching a success action to the reducer.

    2. Redirecting from the service - You can also redirect after the service for example like this:

    myService(): void {
     return this.http.get(url).pipe(
      tap(() => this.router.navigate([navigationURL]))
     )
    }

    1. Other options - other options are possible, in your question you asked if there's something that allows to listen to actions, well that's pretty much what effects are for, to listen to actions, but you could also listen to Observable emittions with subscribe, and redirect when that happens, I DO NOT recommend that approach.

    I think that adding subscriptions is adding unnecessary complexity to the project, because subscriptions have to be managed.

    So what to do?

    Honestly, this is up to you, both options seems valid and I wouldn't say either is a bad practice. I personally prefer to keep my effects clean and do it in the service. I feel like the service is better suited to handle side effects, and it makes it a bit cleaner also when testing, but this is my personal opinion.

    The important thing to remember is, both options are valid and don't add unnecessary complexity.

    There's also one small case which might apply to you, if you need that the user will be in the store before redirecting, it might be better to put this in the effects after dispatching an actions to the reducer. If you're working with Observables it will work either way, but it might be easier to understand if they have tight relationship.