摘要:

  发觉在学习react的生态链中,react+react-router+webpack+es6+fetch等等这些都基本搞懂的差不多了,可以应用到实战当中,唯独这个redux还不能,学习redux还学的挺久的。

  其中困扰我最久的就是redux的异步数据流的处理。难点主要是概念太多,接触的词太多,而且网上的案例看的头都疼,很容易晕,已经晕了好多次了。后来被我简化之后,终于搞懂了,哈哈。!来来来,今天总结一下,希望对大家有所帮助。不过本人主要是介绍redux的异步操作,如果对redux不是很熟悉的,可以看看我之前写的一篇文章http://www.cnblogs.com/xianyulaodi/p/5399264.html

1、redux异步流程简介

先来一张图先,大概介绍一下redux的异步流程:

(图片来自于百度图片)

大概的流程是这样的:

  首先发起一个action,然后通过中间件,这里为什么要用中间件呢,因为这样dispatch的返回值才能是一个函数。通过store.dispatch,将状态的的改变传给store的小弟,reducer,reducer根据action的改变,传递新的状态state。最后将所有的改变告诉给它的大哥,store。store保存着所有的数据,并将数据注入到组件的顶部,这样组件就可以获得它需要的数据了。

2、demo简介

这个demo是我根据官网的async改编的,其实也就是删除了很多其他干扰的东西,让它仅仅是获得一个异步请求。初学嘛,能简单则简单,这样比较明朗。

官网原版的github地址: https://github.com/lewis617/react-redux-tutorial/tree/master/redux-examples/async

我改编的github地址:https://github.com/xianyulaodi/reduxAsync

本文也是通过我自己改的来写的。可以边看代码边看此文,当然可能有些讲的不是很对,欢迎指出

界面如下:

界面很简单,就是通过异步请求,将请求到的数据展现到界面上。可能你会说,不就是一个请求嘛,有啥大不了的。可是这个请求是走了一遍redux的流程的呢,哈哈。

3、代码解析

下面对redux的异步流程做一个小小的解析。期间顺便也回顾一下redux的一些基础知识吧。

3.1 首先从action开始,代码在  action/index.js

 import fetch from 'isomorphic-fetch'
export const RECEIVE_POSTS = 'RECEIVE_POSTS' //获取新闻成功的action
function receivePosts(reddit, json) {
return {
type: RECEIVE_POSTS,
reddit: reddit,
posts: json.data.children.map(child =>child.data)
}
} function fetchPosts(subreddit) { return function (dispatch) { return fetch(`http://www.subreddit.com/r/${subreddit}.json`)
.then(response => response.json())
.then(json =>
dispatch(receivePosts(subreddit, json))
)
}
} //如果需要则开始获取文章
export function fetchPostsIfNeeded(subreddit) { return (dispatch, getState) => { return dispatch(fetchPosts(subreddit)) }
}

  此次的请求没有用ajax,而是用了fetch,这个稍微了解一下即可,入门比较容易。如上面的redux异步流程图一样,它需要借助一个中间件 middleware。其实异步的精髓也就在这个中间件middleware这里了。搞懂了这个,可以说基本也就搞懂的redux的异步了。所以我会花比较大的篇幅去介绍这个中间件。

middleware有什么用?为什么要引入它呢?

在redux里,action仅仅是携带了数据的普通js对象( plain JavaScript objects)。action creator返回的值是这个action类型的对象。然后通过store.dispatch()进行分发……

action ---> dispatcher ---> reducers

如果遇到异步情况,比如点击一个按钮,希望2秒之后更新视图,显示消息“Hi”。我们可能这么写ActionCreator:

var asyncSayActionCreator = function (message) {
setTimeout(function () {
return {
type: 'SAY',
message
}
}, 2000)
}

  这会报错,因为这个asyncSayActionCreator返回的不是一个action,而是一个function。这个返回值无法被reducer识别。

也就是说,正常来说,action返回的是一个对象,而不是一个函数。如果返回函数,会出现错误。

  而异步操作呢,需要action的返回值是一个函数。那么咋办呢,所以需要引入中间件middleware,它在中间起到了桥梁的作用,让action的返回值可以是一个函数,从而传到

reducer那里。也就是说,中间件是用在action发起之后,reducer接收到之前的这个时间段。

也可以这么说,Middleware 主要是负责改变Store中的dispatch方法,从而能处理不同类型的 action 输入,得到最终的 Javascript Plain Object 形式的 action 对象。

  因此,上面那个ActionCreator就可以改写为这样:因为action的返回值是一个函数。

var asyncSayActionCreator = function (message) {
return function (dispatch) {
setTimeout(function () {
dispatch({
type: 'SAY',
message
})
}, 2000)
}
}

中间件是如何工作的

Middleware的中间件有很多,不过我的这个案例只引用了其中的一个,那就是redux-thunk

redux-thunk源码如下:

export default function thunkMiddleware({ dispatch, getState }) {
return next => action =>
typeof action === 'function' ?
action(dispatch, getState) :
next(action);
}

意思是如果action是一个函数,执行这个action函数,如果不是函数,执行next函数。

具体的原理可以看看这两篇文章:

 Redux学习之一:何为middleware?

 
 
回到我的项目源代码:
   fetchPostsIfNeeded这里就是一个中间件。redux-thunk会拦截fetchPostsIfNeeded这个action,会先发起数据请求,如果成功,就将数据传给action.从而到达reducer那里。

再来看看reducer 目录为 reducers/index.js

 import { combineReducers } from 'redux'
import {
RECEIVE_POSTS
} from '../actions' function posts(state = {
items: []
}, action) {
switch (action.type) { case RECEIVE_POSTS:
// Object.assign是ES6的一个语法。合并对象,将对象合并为一个,前后相同的话,后者覆盖强者。详情可以看这里
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
return Object.assign({}, state, {
items: action.posts //数据都存在了这里
})
default:
return state
}
} //废弃、接收到、开始接受新闻后,将state.postsByReddit设为相关参数
function postsByReddit(state = { }, action) {
switch (action.type) {
case RECEIVE_POSTS:
return Object.assign({}, state, {
[action.reddit]: posts(state[action.reddit], action)
})
default:
return state
}
} // 将所有的reducer结合为一个,传给store
const rootReducer = combineReducers({
postsByReddit
}) export default rootReducer

这个跟正常的reducer差不多。判断action的类型,从而根据action的不同类型,返回不同的数据。这里将数据存储在了items这里。这里的reducer只有一个。最后结合成rootReducer,传给store。

store/configureStore.js

 import { createStore, applyMiddleware } from 'redux'
import thunkMiddleware from 'redux-thunk'
import createLogger from 'redux-logger'
import rootReducer from '../reducers' const createStoreWithMiddleware = applyMiddleware(
thunkMiddleware,
createLogger()
)(createStore) export default function configureStore(initialState) {
const store = createStoreWithMiddleware(rootReducer, initialState) if (module.hot) {
// Enable Webpack hot module replacement for reducers
module.hot.accept('../reducers', () => {
const nextRootReducer = require('../reducers')
store.replaceReducer(nextRootReducer)
})
} return store
}
我们是如何在 dispatch 机制中引入 Redux Thunk middleware 的呢?
我们使用了 applyMiddleware(),
const createStoreWithMiddleware = applyMiddleware(
thunkMiddleware,
createLogger()
)(createStore)

其中,createLogger是一个很便捷的 middleware,用来打印 action 日志。通过使用指定的 middleware,action creator 除了返回 action 对象外还可以返回函数。

这时,这个 action creator 就成为了 thunk。

再来看看界面上的调用:在containers/App.js

部分代码:

  //初始化渲染后触发
componentDidMount() {
const { dispatch} = this.props
// 这里可以传两个值,一个是 reactjs 一个是 frontend
dispatch(fetchPostsIfNeeded('frontend'))
}

改变状态的时候也是需要通过dispatch来传递的。

数据的获取是通过provider,将store里面的数据注入给组件。让顶级组件提供给他们的子孙组件调用。代码如下:

import 'babel-core/polyfill'
import React from 'react'
import { render } from 'react-dom'
import { Provider } from 'react-redux'
import App from './containers/App'
import configureStore from './store/configureStore'
const store = configureStore()
render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)

这样就完成了redux的异步操作。其实最主要的区别还是action里面还有中间件的调用,其他的地方基本跟同步的redux差不多的。搞懂了中间件,就基本搞懂了redux的异步操

作。具体的大家还是需要看一下源码和其他相应的文章介绍。当然,demo是不够完善的,因为缺少请求发送失败等等的处理。

再附上一张图来结束今天的学习笔记:

今天的总结就到这里先,感觉自己总结的有点乱。刚好最近用react写了一个小小的项目,下一步将异步和同步结合起来,真正用到实战中,到时候再将这篇博客完善和补充。

由于是初学异步的操作,所以可能有些地方自己总结的错了。有误之处,欢迎指出。如果对于中间件还有很多困惑的同学,不妨看一下下面的链接。

参考:

http://www.aliued.com/?p=3204

http://www.cnblogs.com/bingooo/p/5500108.html#top

http://cn.redux.js.org/

https://segmentfault.com/a/1190000003746223

https://zhuanlan.zhihu.com/p/20597452?utm_source=tuicool&utm_medium=referral 

 

【原】redux异步操作学习笔记的更多相关文章

  1. 【原】flux学习笔记

    最近React(web/native)依旧如火如荼,相信大家都跃跃欲试,入职新公司,现在的团队也开始在React领域有所尝试. 2016年应该是React 逐渐走向成熟的一年.之前在原来公司搞不懂的问 ...

  2. 【原】webpack学习笔记

    之前在react的项目中有用过webpack,不过没有认真的去研究,这段时间又重新好好的学习一下webpack,发觉 webpack是一个很强大的东西.而且很好用,方便,接下来主要是做一下学习的笔记 ...

  3. 【原】Java学习笔记026 - 集合

    package cn.temptation; public class Sample01 { public static void main(String[] args) { // 需求:从三国演义中 ...

  4. 【原】Java学习笔记023 - 字符串缓冲区_正则表达式

    package cn.temptation; import java.util.Arrays; public class Sample01 { public static void main(Stri ...

  5. 【原】Java学习笔记022 - 字符串

    package cn.temptation; public class Sample01 { public static void main(String[] args) { // 字符串 // 定义 ...

  6. 【原】Java学习笔记019 - 面向对象

    package cn.temptation; public class Sample01 { public static void main(String[] args) { // 仔细想一想,Ani ...

  7. 【原】Java学习笔记013 - 阶段测试

    package cn.temptation; import java.util.Scanner; public class Sample01 { public static void main(Str ...

  8. 【原】Java学习笔记004 - 运算符

    package cn.temptation; public class Sample01 { public static void main(String[] args) { // 运算符:对常量 或 ...

  9. 【原】Java学习笔记003 - 数据类型

    package cn.temptation; public class Sample01 { public static void main(String[] args) { System.out.p ...

随机推荐

  1. "此站点已经禁用应用程序"在sharepoint 2013中通过v2013部署app提示该错误

    该错误的原文是:the apps are disabled in this site 可以在yahoo或者bing上搜索这个错误,可以找到解决办法: msdn上也有该错误解决办法,但是如果搜索中文,目 ...

  2. 收藏Javascript中常用的55个经典技巧

    1. oncontextmenu="window.event.returnValue=false" 将彻底屏蔽鼠标右键 <table border oncontextmenu ...

  3. Android NestedScrolling嵌套滑动机制

    Android NestedScrolling嵌套滑动机制 最近项目要用到官网的下拉刷新SwipeRefreshLayout,它是个容器,包裹各种控件实现下拉,不像以前自己要实现事件的拦截,都是通过对 ...

  4. 【BZOJ 3735】苹果树 树上莫队(树分块+离线莫队+鬼畜的压行)

    2016-05-09 UPD:学习了新的DFS序列分块,然后发现这个东西是战术核导弹?反正比下面的树分块不知道要快到哪里去了 #include<cmath> #include<cst ...

  5. [转]IPTABLES中SNAT和MASQUERADE的区别

    IPtables中可以灵活的做各种网络地址转换(NAT)网络地址转换主要有两种:SNAT和DNAT SNAT是source network address translation的缩写即源地址目标转换 ...

  6. css-@keyframes动画

    详细w3c这里 http://www.cnblogs.com/happyPawpaw/archive/2012/09/12/2681348.html Internet Explorer 10.Fire ...

  7. Jquery-选择框点击勾选或者取消

    1:单选框,直接定位到属性名称 $(document).ready(function(){ var old = null; //用来保存原来的对象 $("input[name='sex']& ...

  8. java基础3.0:Java常用API

    本篇介绍Java基础中常用API使用,当然只是简单介绍,围绕重要知识点引入,巩固开发知识,深入了解每个API的使用,查看JavaAPI文档是必不可少的. 一.java.lang包下的API Java常 ...

  9. Get it,你离几何达人不远了!

    对于爱学几何的人,是否存在这样的困扰:没有标准的尺规工具,图形画的不标准,理解上总是出错......整天在纸上画图,浪费大把大把的时间......几何图形画的不美观,在别人面前都拿不出手,公开课上都没 ...

  10. 【2016多校训练4】Multi-University Training Contest 4

    1001  Another Meaning 题意:字符串A中包含的字符串B可以翻译或不翻译,总共有多少方案. 题解:动规,dp[i]表示A的第i位为止有多少方案. 转移方程: dp[i]=dp[i-1 ...