React在Github上已经有接近70000的 star 数了,是目前最热门的前端框架。而我学习React也有一段时间了,现在就开始用 React+Redux 进行实战!

上回说到使用Redux进行状态管理,这次我们使用Redux-saga 管理 Redux 应用异步操作

React 实践项目 (一)
React 实践项目 (二)
React 实践项目 (三)

- 首先我们来看看登陆的 Reducer

  1. export const auth = (state = initialState, action = {}) => {
  2. switch (action.type) {
  3. case LOGIN_USER:
  4. return state.merge({
  5. 'user': action.data,
  6. 'error': null,
  7. 'token': null,
  8. });
  9. case LOGIN_USER_SUCCESS:
  10. return state.merge({
  11. 'token': action.data,
  12. 'error': null
  13. });
  14. case LOGIN_USER_FAILURE:
  15. return state.merge({
  16. 'token': null,
  17. 'error': action.data
  18. });
  19. default:
  20. return state
  21. }
  22. };

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

  1. /**
  2. * Created by Yuicon on 2017/6/27.
  3. */
  4. import {createStore, applyMiddleware } from 'redux';
  5. import createSagaMiddleware from 'redux-saga'
  6. import reducer from '../reducer/reducer';
  7. import rootSaga from '../sagas/sagas';
  8. const sagaMiddleware = createSagaMiddleware();
  9. const store = createStore(
  10. reducer,
  11. applyMiddleware(sagaMiddleware)
  12. );
  13. sagaMiddleware.run(rootSaga);
  14. export default store;

Redux-saga 使用 Generator函数实现

  • 监听 action

创建 src/redux/sagas/sagas.js

  1. /**
  2. * Created by Yuicon on 2017/6/28.
  3. */
  4. import { takeLatest } from 'redux-saga/effects';
  5. import {registerUserAsync, loginUserAsync} from './users';
  6. import {REGISTER_USER, LOGIN_USER} from '../action/users';
  7. export default function* rootSaga() {
  8. yield [
  9. takeLatest(REGISTER_USER, registerUserAsync),
  10. takeLatest(LOGIN_USER, loginUserAsync)
  11. ];
  12. }

我们可以看到在 rootSaga 中监听了两个 action 登陆和注册 。

在上面的例子中,takeLatest 只允许执行一个 loginUserAsync 任务。并且这个任务是最后被启动的那个。 如果之前已经有一个任务在执行,那之前的这个任务会自动被取消。

如果我们允许多个 loginUserAsync 实例同时启动。在某个特定时刻,我们可以启动一个新 loginUserAsync 任务, 尽管之前还有一个或多个 loginUserAsync 尚未结束。我们可以使用 takeEvery 辅助函数。

  • 发起一个 Ajax 请求

  • 获取 Store state 上的数据

selectors.js

  1. /**
  2. * Created by Yuicon on 2017/6/28.
  3. */
  4. export const getAuth = state => state.auth;
  • api

api.js

  1. /**
  2. * Created by Yuicon on 2017/7/4.
  3. * https://github.com/Yuicon
  4. */
  5. /**
  6. * 这是我自己的后台服务器,用 Java 实现
  7. * 项目地址:https://github.com/DigAg/digag-server
  8. * 文档:http://139.224.135.86:8080/swagger-ui.html#/
  9. */
  10. const getURL = (url) => `http://139.224.135.86:8080/${url}`;
  11. export const login = (user) => {
  12. return fetch(getURL("auth/login"), {
  13. method: 'POST',
  14. headers: {
  15. 'Content-Type': 'application/json'
  16. },
  17. body: JSON.stringify(user)
  18. }).then(response => response.json())
  19. .then(json => {
  20. return json;
  21. })
  22. .catch(ex => console.log('parsing failed', ex));
  23. };
  • 创建 src/redux/sagas/users.js
  1. /**
  2. * Created by Yuicon on 2017/6/30.
  3. */
  4. import {select, put, call} from 'redux-saga/effects';
  5. import {getAuth, getUsers} from './selectors';
  6. import {loginSuccessAction, loginFailureAction, registerSuccessAction, registerFailureAction} from '../action/users';
  7. import {login, register} from './api';
  8. import 'whatwg-fetch';
  9. export function* loginUserAsync() {
  10. // 获取Store state 上的数据
  11. const auth = yield select(getAuth);
  12. const user = auth.get('user');
  13. // 发起 ajax 请求
  14. const json = yield call(login.bind(this, user), 'login');
  15. if (json.success) {
  16. localStorage.setItem('token', json.data);
  17. // 发起 loginSuccessAction
  18. yield put(loginSuccessAction(json.data));
  19. } else {
  20. // 发起 loginFailureAction
  21. yield put(loginFailureAction(json.error));
  22. }
  23. }

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 还有很多奇妙的地方,大家可以自行探索。
完整项目代码地址:https://github.com/DigAg/digag-pc-react

React 实践项目 (三)的更多相关文章

  1. React 实践项目 (五)

    React在Github上已经有接近70000的 star 数了,是目前最热门的前端框架.而我学习React也有一段时间了,现在就开始用 React+Redux 进行实战! React 实践项目 (一 ...

  2. React 实践项目 (二)

    React在Github上已经有接近70000的 star 数了,是目前最热门的前端框架.而我学习React也有一段时间了,现在就开始用 React+Redux 进行实战! React 实践项目 (一 ...

  3. android 实践项目三

    android 实践项目三 本周我主要完成的任务是将代码进行整合,然后实现百度地图的定位与搜索功能.在这次实现的 图形界面如下: 在本周的工作中主要的实现出来定位与收索的功能,在地图中能实现了定位,显 ...

  4. React 实践项目 (一)

    React在Github上已经有接近70000的 star 数了,是目前最热门的前端框架.而我学习React也有一段时间了,现在就开始用 React+Redux 进行实战! 项目代码地址:https: ...

  5. python实践项目三:将列表添加到字典

    1.创建一个字典,其中键是字符串,描述一个物品,值是一个整型值,说明有多少该物品.例如,字典值{'rope': 1, 'torch': 6, 'gold coin': 42, 'dagger': 1, ...

  6. 总结 React 组件的三种写法 及最佳实践 [涨经验]

    React 专注于 view 层,组件化则是 React 的基础,也是其核心理念之一,一个完整的应用将由一个个独立的组件拼装而成. 截至目前 React 已经更新到 v15.4.2,由于 ES6 的普 ...

  7. Immutable.js 以及在 react+redux 项目中的实践

    来自一位美团大牛的分享,相信可以帮助到你. 原文链接:https://juejin.im/post/5948985ea0bb9f006bed7472?utm_source=tuicool&ut ...

  8. 总结 React 组件的三种写法 及最佳实践

    React 专注于 view 层,组件化则是 React 的基础,也是其核心理念之一,一个完整的应用将由一个个独立的组件拼装而成. 截至目前 React 已经更新到 v15.4.2,由于 ES6 的普 ...

  9. Expo大作战(三)--针对已经开发过react native项目开发人员有针对性的介绍了expo,expo的局限性,开发时项目选型注意点等

    简要:本系列文章讲会对expo进行全面的介绍,本人从2017年6月份接触expo以来,对expo的研究断断续续,一路走来将近10个月,废话不多说,接下来你看到内容,讲全部来与官网 我猜去全部机翻+个人 ...

随机推荐

  1. 可视化之Berkeley Earth

    去年冬天雾霾严重的那几天,写了两篇关于空气质量的文章,<可视化之PM2.5>和<谈谈我对雾霾的认识>.坦白说,环境问题是一个无法逃避又无能为力的话题.最近因为工作中有一些数据可 ...

  2. OVS + dpdk 安装与实验环境配置

    ***DPDK datapath的OVS的安装与实验环境配置 首先肯定是DPDK的安装       0:安装必要的工具            make            gcc           ...

  3. 如何编写Spring-Boot自动配置

    摘要 本文主要介绍如何把一个spring的项目(特别是一些公共工具类项目),基于spring boot的自动配置的思想封装起来,使其他Spring-Boot项目引入后能够进行快速配置. AutoCon ...

  4. (转)addEventListener()与removeEventListener()详解

    转自:http://www.111cn.net/wy/js-ajax/48004.htm addEventListener()与removeEventListener()用于处理指定和删除事件处理程序 ...

  5. Bash环境配置文件

    一.环境配置文件读取优先级 其中~/.bash_profile,~/.bash_login,~/.profile三个文件只有一个有效,查找优先级从左至右降低.bash会一直检查是否有~/.bashrc ...

  6. NodeJS 实现手机短信验证 模块阿里大于

    1,NodeJS 安装阿里大于模块 切换到项目目录使用npm 安装阿里于模块 npm i node-alidayu --save 2,aliyu官网使用淘宝账户登录 登录阿里大于 https://do ...

  7. Behavior的使用(一):页面跳转NavigateToPageAction

    Behavior的使用,让UI设计师能够更加方便的进行UI设计,更高效地和开发进行合作.Behavior有三种触发方式:EventTriggerBehavior事件触发,DataTriggerBeha ...

  8. JS性能优化之怎么加载JS文件

    IE8+等实行并行下载,各JS下载不受影响,但仍阻塞其他资源下载 如: 图片 所以首要规则就是:将JS放在body底部(推荐) 加载100kb的单个文件比4个25kb的文件快(减少外链文件数量)(脚本 ...

  9. 转化来的图标用法symbol引用‘font-class引用及Unicode引用

  10. App测试札记

    App测试札记 测试应该收集信息 测试应该问问题 测试应该扮演不同角色 测试应该如实反馈 初学者 有哪些可以利用的信息?需求,技术方案,测试设计,现有功能,相关人员 App会在哪些环境下运行 App会 ...