Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 4834464

Browse files
author
Tian, ShaoQin
committed
refactor to use redux-saga for login/logout flow
1 parent feb78e7 commit 4834464

File tree

8 files changed

+118
-101
lines changed

8 files changed

+118
-101
lines changed

‎client/package.json‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
"redux-form": "^7.4.2",
5151
"redux-logger": "^3.0.6",
5252
"redux-promise-middleware": "^5.1.1",
53+
"redux-saga": "^0.16.0",
5354
"redux-thunk": "^2.3.0",
5455
"simple-line-icons": "^2.4.1",
5556
"subscriptions-transport-ws": "^0.9.9"

‎client/src/api/auth.js‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import axios from 'axios'
2+
3+
export const login = (username, password) => {
4+
return axios.post(`/signin`, {username, password})
5+
}
6+
7+
export const me = () => {
8+
return axios.get(`/me`)
9+
}
10+
11+
export const register = (username, password) => {
12+
return axios.post(`/signup`, {username, password})
13+
}

‎client/src/middlewares/axiosInterceptors.js‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { push } from 'connected-react-router'
44

55
axios.defaults.baseURL = `http://${config.api_host}:${config.api_port}`
66

7-
const setupAxiosInterceptors = (onUnauthenticated,store) => {
7+
const setupAxiosInterceptors = (store) => {
88
const onRequestSuccess = config => {
99
var token = localStorage.getItem('auth-token');
1010
if (token) {
@@ -18,7 +18,7 @@ const setupAxiosInterceptors = (onUnauthenticated, store) => {
1818
if (!error.response) {
1919

2020
} else if (error.response.status === 403 || error.response.status === 401) {
21-
//onUnauthenticated();
21+
2222
} else if (error.response.status === 404) {
2323
store.dispatch(push('/404'));
2424
} else if (error.response.status === 500) {

‎client/src/modules/auth.js‎

Lines changed: 15 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,29 @@
1-
import axios from 'axios'
21
import update from 'immutability-helper';
3-
import { getCurrentUser, CLEAR_USER } from './user'
4-
import { REQUEST, SUCCESS, FAILURE } from './actionType'
5-
import { SubmissionError } from 'redux-form'
62

7-
export const AUTH_USER = 'AUTH_USER'
8-
export const AUTH_USER_2 = 'AUTH_USER_2'
9-
export const UNAUTH_USER = 'UNAUTH_USER'
3+
export const REQUEST_LOGIN = 'REQUEST_LOGIN'
4+
export const REQUEST_LOGOUT = 'REQUEST_LOGOUT'
5+
6+
export const SET_TOKEN = 'SET_TOKEN'
7+
export const SET_AUTHENTICATED = 'SET_AUTHENTICATED'
8+
export const SET_UNAUTHENTICATED = 'SET_UNAUTHENTICATED'
109

1110
export default function authReducer(state = {authenticated: false, token: null}, action) {
1211
switch (action.type) {
13-
case SUCCESS(AUTH_USER):
14-
return update(state, {authenticated: {$set: true},token: {$set: action.payload.data.token}})
15-
case AUTH_USER_2:
12+
case SET_TOKEN:
13+
return update(state, {token: {$set: action.payload.data.token}})
14+
case SET_AUTHENTICATED:
1615
return update(state, {authenticated: {$set: true}})
17-
case UNAUTH_USER:
16+
case SET_UNAUTHENTICATED:
1817
return update(state, {authenticated: {$set: false}, token: {$set: null}})
1918
default:
2019
return state
2120
}
2221
}
2322

24-
export const authenticated = () => ({
25-
type: AUTH_USER_2,
26-
payload: null
27-
})
28-
29-
export const login = ({username, password}) => dispatch =>
30-
dispatch({
31-
type: AUTH_USER,
32-
payload: axios.post(`/signin`, {username, password})
33-
}).then(({value, action}) => {
34-
localStorage.setItem('auth-token', value.data.token)
35-
dispatch(getCurrentUser())
36-
}).catch((error) => {
37-
throw new SubmissionError({_error: 'Bad Login'});
38-
})
39-
40-
export const register = ({username, password}) => dispatch =>
41-
dispatch({
42-
type: AUTH_USER,
43-
payload: axios.post(`/signup`, {username, password})
44-
}).then(({value, action}) => {
45-
localStorage.setItem('auth-token', value.data.token)
46-
dispatch(getCurrentUser());
47-
// TODO: init global variables
48-
})
49-
50-
export const signoutUser = () => dispatch => {
51-
localStorage.removeItem('auth-token');
52-
dispatch({type: 'USER_LOGOUT'});
23+
export function loginRequest ({username, password}) {
24+
return {type: REQUEST_LOGIN, payload: {username, password}}
5325
}
5426

55-
export const clearAuthToken = () => {
56-
localStorage.removeItem('auth-token');
57-
};
58-
27+
export const signoutUser = () => {
28+
return ({type: REQUEST_LOGOUT});
29+
}

‎client/src/modules/user.js‎

Lines changed: 3 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
1-
import axios from 'axios'
2-
import { REQUEST, SUCCESS, FAILURE } from './actionType'
1+
import { REQUEST, SUCCESS } from './actionType'
32
import update from 'immutability-helper';
4-
import { authenticated, clearAuthToken } from './auth'
5-
import { push } from 'connected-react-router'
63

74
export const FETCH_ME = 'FETCH_ME'
85
export const UPDATE_ME = 'UPDATE_ME'
@@ -27,43 +24,6 @@ export default function userReducer(state = {user: null, loading: false}, action
2724
}
2825
}
2926

30-
export const getCurrentUser = () => dispatch => {
31-
dispatch({
32-
type: FETCH_ME,
33-
payload: axios.get('/me'),
34-
/*meta: {
35-
successMessage: '已成功载入登录信息!'
36-
}*/
37-
}).then(({value, action}) => {
38-
let user = value.data;
39-
if (!user) {
40-
clearAuthToken();
41-
dispatch(push('login'));
42-
return;
43-
}
44-
dispatch(authenticated());
45-
// TODO: init global variables
46-
}).catch((error) => {
47-
dispatch(push('login'));
48-
})
49-
}
50-
51-
export const updateUser = (user) => {
52-
return {
53-
type: UPDATE_ME,
54-
payload: axios.post('/update', {user})
55-
}
56-
}
57-
58-
export const changePassword = (oldPassword, newPassword) => {
59-
return {
60-
type: CHANGE_PASSWORD,
61-
payload: axios.post('/changePassword', {oldPassword, newPassword})
62-
}
63-
}
64-
65-
export function clearUser() {
66-
return {
67-
type: CLEAR_USER
68-
}
27+
export const getCurrentUser = () => {
28+
return {type: FETCH_ME}
6929
}

‎client/src/sagas/index.js‎

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import {take, call, put, fork, race} from 'redux-saga/effects'
2+
import { login, me, register } from '../api/auth'
3+
import { REQUEST_LOGIN, REQUEST_LOGOUT, SET_TOKEN, SET_AUTHENTICATED, SET_UNAUTHENTICATED } from '../modules/auth'
4+
import { REQUEST, SUCCESS } from '../modules/actionType'
5+
import { FETCH_ME, CLEAR_USER } from '../modules/user'
6+
import { push } from 'connected-react-router'
7+
import { showLoading, hideLoading } from 'react-redux-loading-bar'
8+
9+
export function * unAuth() {
10+
localStorage.removeItem('auth-token');
11+
yield put({type: SET_UNAUTHENTICATED});
12+
yield put({type: CLEAR_USER});
13+
yield put(push('/login'))
14+
}
15+
16+
export function * fetchUser() {
17+
try {
18+
yield put({type: REQUEST(FETCH_ME)})
19+
let response = yield call(me)
20+
yield put({type: SUCCESS(FETCH_ME), payload: response})
21+
yield put({type: SET_AUTHENTICATED});
22+
} catch (error) {
23+
yield call(unAuth)
24+
}
25+
}
26+
27+
export function * authorize ({username, password}) {
28+
yield put(showLoading())
29+
30+
try {
31+
let response = yield call(login, username, password)
32+
localStorage.setItem('auth-token', response.data.token)
33+
yield put({type: SET_TOKEN, payload: response})
34+
yield call(fetchUser)
35+
yield put(push('/dashboard'))
36+
} catch (error) {
37+
yield call(unAuth)
38+
} finally {
39+
yield put(hideLoading())
40+
}
41+
}
42+
43+
export function * loginFlow () {
44+
while (true) {
45+
const action = yield take(REQUEST_LOGIN)
46+
const {username, password} = action.payload
47+
yield call(authorize, {username, password})
48+
}
49+
}
50+
51+
export function * fetchMeFlow () {
52+
while (true) {
53+
yield take(FETCH_ME)
54+
yield put(showLoading())
55+
yield call(fetchUser)
56+
yield put(hideLoading())
57+
}
58+
}
59+
60+
export function * logoutFlow () {
61+
while (true) {
62+
yield take(REQUEST_LOGOUT)
63+
localStorage.removeItem('auth-token');
64+
yield put({type: 'RESET_REDUX'})
65+
yield put(push('/login'))
66+
}
67+
}
68+
69+
export default function* rootSaga() {
70+
yield fork(loginFlow)
71+
yield fork(fetchMeFlow)
72+
yield fork(logoutFlow)
73+
}

‎client/src/store.js‎

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,24 @@
11
import { createStore, applyMiddleware, compose } from 'redux'
22
import { connectRouter, routerMiddleware } from 'connected-react-router'
33
import thunk from 'redux-thunk'
4+
import createSagaMiddleware from 'redux-saga'
45
import promiseMiddleware from 'redux-promise-middleware';
56
import { loadingBarMiddleware } from 'react-redux-loading-bar';
67
import createHistory from 'history/createBrowserHistory'
78
import { createLogger } from 'redux-logger'
89
import appReducer from './modules'
910
import { setupAxiosInterceptors } from './middlewares/axiosInterceptors';
1011
import notificationMiddleware from './middlewares/notification-middleware';
11-
import { UNAUTH_USER, AUTH_ERROR } from './modules/auth'
1212
import { getCurrentUser } from './modules/user'
13+
import rootSaga from './sagas'
1314

1415
export const history = createHistory()
1516

1617
export default (initialState) => {
1718

1819
const enhancers = []
19-
const middleware = [thunk, notificationMiddleware, promiseMiddleware(), loadingBarMiddleware(), routerMiddleware(history)]
20+
const sagaMiddleware = createSagaMiddleware()
21+
const middleware = [thunk, sagaMiddleware, notificationMiddleware, promiseMiddleware(), loadingBarMiddleware(), routerMiddleware(history)]
2022

2123
if (process.env.NODE_ENV === 'development') {
2224
const devToolsExtension = window.__REDUX_DEVTOOLS_EXTENSION__
@@ -41,7 +43,7 @@ export default (initialState) => {
4143
}
4244

4345
const appReducer2 = (state, action) => {
44-
if (action.type === 'USER_LOGOUT') {
46+
if (action.type === 'RESET_REDUX') {
4547
const { routing } = state
4648
state = { routing }
4749
}
@@ -56,13 +58,9 @@ export default (initialState) => {
5658
composedEnhancers
5759
)
5860

59-
const logoutCallback = ()=> {
60-
store.dispatch({type: 'USER_LOGOUT'});
61-
localStorage.removeItem('auth-token');
62-
history.push('login');
63-
}
61+
sagaMiddleware.run(rootSaga)
6462

65-
setupAxiosInterceptors(logoutCallback,store);
63+
setupAxiosInterceptors(store);
6664

6765
if (token) {
6866
// get user info if the token exists

‎client/src/views/Pages/Login/Login.js‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,13 @@ import { connect } from 'react-redux'
44
import { bindActionCreators } from 'redux'
55
import { Redirect } from 'react-router-dom'
66
import LoginForm from './LoginForm'
7-
import { login } from '../../../modules/auth'
7+
import { loginRequest } from '../../../modules/auth'
88

99
class Login extends Component {
1010

1111
handleSubmit({username, password}) {
12-
return this.props.login({username, password})
12+
//return this.props.login({username, password})
13+
return this.props.loginRequest({username, password})
1314
}
1415

1516
render() {
@@ -47,7 +48,7 @@ class Login extends Component {
4748
}
4849
}
4950

50-
const mapDispatchToProps = { login }
51+
const mapDispatchToProps = { loginRequest }
5152

5253
function mapStateToProps(state) {
5354
return {

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /