React 實踐項目 (三) Redux-Saga

React在Github上已經有接近70000的 star 數了,是目前最熱門的前端框架。而我學習React也有一段時間了,如今就開始用 React+Redux 進行實戰!html

上回說到使用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。react

具體到這個登錄功能就是咱們在登錄彈窗點擊登錄時會發出一個 LOGIN_USER action,Sagas 監聽到 LOGIN_USER action,發起一個 Ajax 請求到後臺,根據結果決定發起 LOGIN_USER_SUCCESS action 仍是LOGIN_USER_FAILURE actiongit

接下來,咱們來實現這個流程es6

  • 建立 Saga middleware 鏈接至 Redux store

在 package.json 中添加 redux-saga 依賴github

"redux-saga": "^0.15.4"ajax

修改 src/redux/store/store.jsjson

/**
 * 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 函數實現redux

  • 監聽 action

建立 src/redux/sagas/sagas.jsapi

/**
 * 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 到 Store
call(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 還有不少奇妙的地方,你們能夠自行探索。

完整項目代碼地址

相關文章
相關標籤/搜索