23 July 2019
Angular (38) -- NgRx Effects
by Jerry Zhang
LeetCode Day 18: P141. Linked List Cycle (Easy)
题目:
给一个linked list, 检查是否有环。要求O(1)
我的思路:
一开始想每经过一个节点,把地址记到HashSet里。可是时间复杂度会非常高。放弃了。
最优解:
参考知乎一篇文章:
https://zhuanlan.zhihu.com/p/38521018
快慢指针:首先,使用快慢指针判断链表中是否有环就好比是郭小明和博尔特赛跑,如果两人在笔直的道路上赛跑(链表不含有环), 那么从同一起点出发后,郭小明永远追不上(遇不到)博尔特;如果是类似马拉松比赛,最后是在体育馆中的环形跑道跑完(假设无限跑), 那么博尔特总会在环形跑道中“追上”郭小明。
public class Solution {
public boolean hasCycle(ListNode head) {
if (head == null) {
return false;
}
ListNode slow = head;
ListNode fast = head;
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
if (slow == fast) {
return true;
}
}
return false;
}
}
Angular: NgRx Effects
Add an effect for signing up
Add a new action SIGNUP_START
export const SIGNUP_START = '[Auth] Signup Start';
export class SignupStart implements Action {
readonly type = SIGNUP_START;
constructor(public payload: { email: string; password: string}) {}
}
Add a new effect authSignup
@Effect()
authSignup = this.actions$.pipe(
ofType(AuthActions.SIGNUP_START),
switchMap((signupAction: AuthActions.SignupStart) => {
return this.http.post<AuthResponseData>(
'https://www.googleapis.com/identitytoolkit/v3/relyingparty/signupNewUser?key=AIzaSyCQUonbyGcWNNCk-jS2WWkgpR3U0TtkIdA',
{
email: signupAction.payload.email,
password: signupAction.payload.password,
returnSecureToken: true
})
.pipe(
tap(resData => {
this.authService.setLogoutTimer(+resData.expiresIn * 1000);
}),
map(resData => {
return handleAuthentication(
+resData.expiresIn,
resData.email,
resData.localId,
resData.idToken
);
}),
catchError(errorRes => {
return handleError(errorRes);
})
);
})
);
Dispatch a new SignupStart
action in auth.component.ts
.
if (this.isLoginMode) {
this.store.dispatch(
new AuthActions.LoginStart({email: email, password: password})
);
} else {
this.store.dispatch(
new AuthActions.SignupStart({email: email, password: password})
);
}
Fix some bugs
In the reducer, return the same state when we login start and signup start
case AuthActions.LOGIN_START:
case AuthActions.SIGNUP_START:
return {
...state,
authError: null,
loading: true
};
Unsubscribe the store subscription:
In auth.component.ts
:
private storeSub: Subscription;
ngOnInit(): void {
this.storeSub = this.store.select('auth').subscribe(authState => {
this.isLoading = authState.loading;
this.error = authState.authError;
if (this.error) {
this.showErrorAlert(this.error);
}
});
}
ngOnDestroy(): void {
if (this.closeSub) {
this.closeSub.unsubscribe();
}
if (this.storeSub) {
this.storeSub.unsubscribe();
}
}
Add a new action ClearError.
export const CLEAR_ERROR = '[Auth] Clear Error';
export class ClearError implements Action {
readonly type = CLEAR_ERROR;
}
In the reducer:
case AuthActions.CLEAR_ERROR:
return {
...state,
authError: null
};
In auth.component.ts
:
onHandleError() {
this.store.dispatch(new AuthActions.ClearError());
}
LocalStorage in NgRx
Create a new Action, AUTO_LOGIN
export const AUTO_LOGIN = '[Auth] Auto Login';
export class AutoLogin implements Action {
readonly type = AUTO_LOGIN;
}
Set a user in local storage in handleAuthentication
const handleAuthentication = (
expiresIn: number,
email: string,
userId: string,
token: string
) => {
const expirationDate = new Date(
new Date().getTime() + expiresIn * 1000
);
const user = new User(email, userId, token, expirationDate);
localStorage.setItem('userData', JSON.stringify(user));
return new AuthActions.AuthenticateSuccess({
email: email,
userId: userId,
token: token,
expirationDate: expirationDate
});
};
Add a new effect authLogout
@Effect({dispatch: false})
authLogout = this.actions$.pipe(
ofType(AuthActions.LOGOUT),
tap(() => {
this.authService.clearLogoutTimer();
localStorage.removeItem('userData');
this.router.navigate(['/auth']);
})
);
Add a new effect autoLogin
@Effect()
autoLogin = this.actions$.pipe(
ofType(AuthActions.AUTO_LOGIN),
map(() => {
const userData: {
email: string;
id: string;
_token: string;
_tokenExpirationDate: string;
} = JSON.parse(localStorage.getItem('userData'));
if (!userData) {
return { type: 'DUMMY'};
}
const loadedUser = new User(
userData.email,
userData.id,
userData._token,
new Date(userData._tokenExpirationDate)
);
if (loadedUser.token) {
// this.user.next(loadedUser);
const expirationDuration = new Date(userData._tokenExpirationDate).getTime() - new Date().getTime();
this.authService.setLogoutTimer(expirationDuration);
return new AuthActions.AuthenticateSuccess({
email: loadedUser.email,
userId: loadedUser.id,
token: loadedUser.token,
expirationDate: new Date(userData._tokenExpirationDate)
});
// const expirationDuration = new Date(userData._tokenExpirationDate).getTime() - new Date().getTime();
// this.autoLogout(expirationDuration);
}
return { type: 'DUMMY'};
})
);
Dispatch auto login in app component
ngOnInit(): void {
this.store.dispatch(new AuthActions.AutoLogin());
this.loggingService.printLog('Hello from AppComponent ngOnInit');
}