React在Github上已經有接近70000的 star 數了,是目前最熱門的前端框架。而我學習React也有一段時間了,現在就開始用 React+Redux 進行實戰! 上回說到使用Redux進行狀態管理,這次我們使用Redux-saga 管理 Redux 應用非同步操作 React 實踐項目 ( ...
React在Github上已經有接近70000的 star 數了,是目前最熱門的前端框架。而我學習React也有一段時間了,現在就開始用 React+Redux 進行實戰!
上回說到使用Redux進行狀態管理,這次我們使用Redux-saga 管理 Redux 應用非同步操作
React 實踐項目 (一)
React 實踐項目 (二)
React 實踐項目 (三)
- 首先我們來看看登陸的 Reducer
export const auth = (state = initialState, action = {}) => {
switch (action.type) {
case LOGIN_USER:
return state.merge({
'user': action.data,
'error': null,
'token': null,
});
case LOGIN_USER_SUCCESS:
return state.merge({
'token': action.data,
'error': null
});
case LOGIN_USER_FAILURE:
return state.merge({
'token': null,
'error': action.data
});
default:
return state
}
};
Sagas 監聽發起的 action,然後決定基於這個 action 來做什麼:是發起一個非同步調用(比如一個 Ajax 請求),還是發起其他的 action 到 Store,甚至是調用其他的 Sagas。
具體到這個登陸功能就是我們在登陸彈窗點擊登陸時會發出一個 LOGIN_USER
action,Sagas 監聽到 LOGIN_USER
action,發起一個 Ajax 請求到後臺,根據結果決定發起 LOGIN_USER_SUCCESS
action 還是LOGIN_USER_FAILURE
action
接下來,我們來實現這個流程
-
創建 Saga middleware 連接至 Redux store
在 package.json 中添加 redux-saga
依賴
"redux-saga": "^0.15.4"
修改 src/redux/store/store.js
/**
* Created by Yuicon on 2017/6/27.
*/
import {createStore, applyMiddleware } from 'redux';
import createSagaMiddleware from 'redux-saga'
import reducer from '../reducer/reducer';
import rootSaga from '../sagas/sagas';
const sagaMiddleware = createSagaMiddleware();
const store = createStore(
reducer,
applyMiddleware(sagaMiddleware)
);
sagaMiddleware.run(rootSaga);
export default store;
Redux-saga 使用 Generator 函數實現
-
監聽 action
創建 src/redux/sagas/sagas.js
/**
* Created by Yuicon on 2017/6/28.
*/
import { takeLatest } from 'redux-saga/effects';
import {registerUserAsync, loginUserAsync} from './users';
import {REGISTER_USER, LOGIN_USER} from '../action/users';
export default function* rootSaga() {
yield [
takeLatest(REGISTER_USER, registerUserAsync),
takeLatest(LOGIN_USER, loginUserAsync)
];
}
我們可以看到在 rootSaga 中監聽了兩個 action 登陸和註冊 。
在上面的例子中,takeLatest 只允許執行一個 loginUserAsync 任務。並且這個任務是最後被啟動的那個。 如果之前已經有一個任務在執行,那之前的這個任務會自動被取消。
如果我們允許多個 loginUserAsync 實例同時啟動。在某個特定時刻,我們可以啟動一個新 loginUserAsync 任務, 儘管之前還有一個或多個 loginUserAsync 尚未結束。我們可以使用 takeEvery 輔助函數。
-
發起一個 Ajax 請求
-
獲取 Store state 上的數據
selectors.js
/**
* Created by Yuicon on 2017/6/28.
*/
export const getAuth = state => state.auth;
- api
api.js
/**
* Created by Yuicon on 2017/7/4.
* https://github.com/Yuicon
*/
/**
* 這是我自己的後臺伺服器,用 Java 實現
* 項目地址:https://github.com/DigAg/digag-server
* 文檔:http://139.224.135.86:8080/swagger-ui.html#/
*/
const getURL = (url) => `http://139.224.135.86:8080/${url}`;
export const login = (user) => {
return fetch(getURL("auth/login"), {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(user)
}).then(response => response.json())
.then(json => {
return json;
})
.catch(ex => console.log('parsing failed', ex));
};
- 創建 src/redux/sagas/users.js
/**
* Created by Yuicon on 2017/6/30.
*/
import {select, put, call} from 'redux-saga/effects';
import {getAuth, getUsers} from './selectors';
import {loginSuccessAction, loginFailureAction, registerSuccessAction, registerFailureAction} from '../action/users';
import {login, register} from './api';
import 'whatwg-fetch';
export function* loginUserAsync() {
// 獲取Store state 上的數據
const auth = yield select(getAuth);
const user = auth.get('user');
// 發起 ajax 請求
const json = yield call(login.bind(this, user), 'login');
if (json.success) {
localStorage.setItem('token', json.data);
// 發起 loginSuccessAction
yield put(loginSuccessAction(json.data));
} else {
// 發起 loginFailureAction
yield put(loginFailureAction(json.error));
}
}
select(selector, ...args)
用於獲取Store state 上的數據put(action)
發起一個 action 到 Storecall(fn, ...args)
調用 fn 函數並以 args 為參數,如果結果是一個 Promise,middleware 會暫停直到這個 Promise 被 resolve,resolve 後 Generator 會繼續執行。 或者直到 Promise 被 reject 了,如果是這種情況,將在 Generator 中拋出一個錯誤。
Redux-saga 詳細api文檔
-
結語
我在工作時用的是 Redux-Thunk, Redux-Thunk 相對來說更容易實現和維護。但是對於複雜的操作,尤其是面對複雜非同步操作時,Redux-saga 更有優勢。到此我們完成了一個 Redux-saga 的入門教程,Redux-saga 還有很多奇妙的地方,大家可以自行探索。
完整項目代碼地址:https://github.com/DigAg/digag-pc-react