视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
关于React实践项目的示例详解(三)
2020-11-27 20:16:19 责编:小采
文档

上回说到使用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_SUCCESSaction 还是LOGIN_USER_FAILUREaction

接下来,我们来实现这个流程

  • 创建 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.
     * 
     */
    
    /**
     * 这是我自己的后台服务器,用 Java 实现
     * 项目地址:
     * 文档: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 还有很多奇妙的地方,

    下载本文
    显示全文
    专题