[React] 12 - Redux: async & middleware
这里只是简单地了解中间件的概念,对于异步,貌似之后要讲的saga更胜一筹。
reducer计算新状态的策略:
- Action 发出以后,Reducer 立即算出 State,这叫做同步;
- Action 发出以后,过一段时间再执行 Reducer,这就是异步。
何为中间件
一、安插中间件的位置
middleware提供的是位于 action 被发起之后,到达 reducer 之前的扩展点。
- 原程序
const Counter = ({ value, onIncrement, onDecrement }) => (
<div>
<h1>{value}</h1>
<button onClick={onIncrement}>+</button>
<button onClick={onDecrement}>-</button>
</div>
); ............................................................... /**
* 更新过程:
* action + old state ==> new state
* 也可以考虑combineReducers的形式
*/
const reducer = (state = 0, action) => {
switch (action.type) {
case 'INCREMENT': return state + 1; // step 2: how to implement this action --> automatically trigger step 3: UI
case 'DECREMENT': return state - 1;
default: return state;
}
}; ...............................................................
const store = createStore(reducer);
/**
* 渲染过程:
* 既接收动作,也处理界面更新
* 当然,具体的更新html还要归于具体的html代码,也就是最上面的Counter组件的定义
*/
const render = () => {
ReactDOM.render(
<Counter
value={store.getState()} // step 3: render new ui based on new state
onIncrement={() =>store.dispatch({type: 'INCREMENT'})} // step 1: receive action --> automatically trigger step 2: reducer
onDecrement={() => store.dispatch({type: 'DECREMENT'})}
/>,
document.getElementById('root')
);
}; render();
store.subscribe(render);
- 功能增强
对store.dispatch做了新的定义:
--- 不仅给store发送信号action。
--- 而且附带了log的功能。
let next = store.dispatch;
store.dispatch = function dispatchAndLog(action) {
console.log('dispatching', action);
next(action);
console.log('next state', store.getState());
}
再理解如下文字:
(1)Reducer:纯函数,只承担计算 State 的功能,不合适承担其他功能,也承担不了,因为理论上,纯函数不能进行读写操作。
(2)View:与 State 一一对应,可以看作 State 的视觉层,也不合适承担其他功能。
(3)Action:消息的载体,让reducer的纯函数去操作,自己不用干活er。
二、中间件的用法
作为createStore的参数来注册。
applyMiddlewares(...),Redux 的原生方法,作用是将所有中间件组成一个数组,依次执行。
const store = createStore(
reducer,
initial_state, // 有第二参数则表示整个程序的初始状态
applyMiddleware(logger)
);
const store = createStore(
reducer,
applyMiddleware(thunk, promise, logger) // 这里的log一定要放在最后
);
三、实例分析
通过中间件,增强Log的用法。
- 在reducer之前执行
store.dispatch ==> middleware --> logger ==> "action fired."
- 添加 next(action)
其实就是一个多层嵌套返回函数的函数,
柯里化 - 使用箭头的写法在函数式编程,对柯里化更详细的介绍可以看一看这篇 张鑫旭的博客。
第一个(最外层)方法的参数是一个包含dispatch和getState字段(方法)的对象,其实就是store对象,所以也可以写成:
# 一个Redux中间件的基本写法 store => next => action => {
...
};
参数next是一个方法,作用是:通知下一个Redux中间件对这次的action进行处理。
next(action)
,如果一个中间件中没有执行next(action)
,则action会停止向后续的中间件传递,并阻止reducer的执行(store将不会因为本次的action而更新)。
import { applyMiddleware, createStore } from "redux"; const reducer = (initialState=0, action) => {
if (action.type === "INC") {
return initialState + 1;
} else if (action.type === "DEC") {
return initialState - 1;
} else if (action.type === "MULT") {
throw new Error("AHHHH!!");
}
return initialState;
}
-------------------------------------------------------------
const logger = (store) => (next) => (action) => {
console.log("Logged", action);
return next(action);
}; const errorHandler = (store) => (next) => (action) => {
try {
return next(action);
} catch(e) {
console.log("ERROR!", e);
}
}; const middleware= applyMiddleware(
logger,
errorHandler
) -------------------------------------------------------------
const store = createStore(reducer, middleware) store.subscribe(() => {
console.log("store changed", store.getState());
}) store.dispatch({type: "INC"})
store.dispatch({type: "INC"})
store.dispatch({type: "INC"})
store.dispatch({type: "DEC"})
store.dispatch({type: "DEC"})
store.dispatch({type: "DEC"})
store.dispatch({type: "MULT"})
store.dispatch({type: "DEC"})
- redux-logger 使用
异步操作实现
一、用户送出第一个 Action
Ref: RUAN的博文可能更容易理解些
如果发送的信号(action)涉及到服务端,那么异步就是不可避免的事情。
- 整个异步操作的思路:
第一个action:操作开始时,送出一个 Action,触发 State 更新为"正在操作"状态,View 重新渲染
第二个action:操作结束后,再送出一个 Action,触发 State 更新为"操作结束"状态,View 再一次重新渲
- 异步需要三种action,三种 Action 可以有两种不同的写法:
// 写法一:名称相同,参数不同
{ type: 'FETCH_POSTS' }
{ type: 'FETCH_POSTS', status: 'error', error: 'Oops' }
{ type: 'FETCH_POSTS', status: 'success', response: { ... } } // 写法二:名称不同
{ type: 'FETCH_POSTS_REQUEST' }
{ type: 'FETCH_POSTS_FAILURE', error: 'Oops' }
{ type: 'FETCH_POSTS_SUCCESS', response: { ... } }
- State 也要进行改造,反映不同的操作状态:
let state = {
// ...
isFetching: true, // 表示是否在抓取数据
didInvalidate: true, // 表示数据是否过时
lastUpdated: 'xxxxxxx' // 表示上一次更新时间
};
二、自动送出第二个 Action
- 代码背景
异步操作至少要送出两个 Action:
- 用户触发第一个 Action,这个跟同步操作一样,没有问题;
- 如何才能在操作结束时,系统自动送出第二个 Action 呢?
# 异步组件的例子 class AsyncApp extends Component {
componentDidMount() {
const { dispatch, selectedPost } = this.props
dispatch(fetchPosts(selectedPost)) // fetchPosts是送给server的信号(action)
} // ...
- Step 1 - 返回的是一个函数,而非”对象”
What kind of ActionCreator it is?
(1) 先发出一个Action(requestPosts(postTitle)
) ----> 然后进行异步操作。
(2) 拿到结果后,先将结果转成 JSON 格式 ----> 然后再发出一个 Action( receivePosts(postTitle, json)
)。
const fetchPosts = postTitle => (dispatch, getState) => {
dispatch(requestPosts(postTitle));
return fetch(`/some/API/${postTitle}.json`)
.then(response => response.json())
.then(json => dispatch(receivePosts(postTitle, json)));
};
};
- Step 2 - 如何使用
// 使用方法一
store.dispatch( fetchPosts('reactjs') );
// 使用方法二
store.dispatch( fetchPosts('reactjs') ).then(() =>
console.log(store.getState())
);
返回的函数的参数:dispatch
和getState
这两个 Redux 方法,而非 Action 的内容。
- 注意事项
(1)fetchPosts
返回了一个函数,而普通的 Action Creator 默认返回一个对象。
(2)返回的函数的参数是dispatch
和getState
这两个 Redux 方法,普通的 Action Creator 的参数是 Action 的内容。
(3)在返回的函数之中,先发出一个 Action(requestPosts(postTitle)
),表示操作开始。
(4)异步操作结束之后,再发出一个 Action(receivePosts(postTitle, json)
),表示操作结束。
三、中间件帮助"参数扩展"
- redux-thunk:方案一,使参数支持 “函数"
通过中间件redux-thunk,改造store.dispatch
,使得后者可以接受函数作为参数。
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import reducer from './reducers'; // Note: this API requires redux@>=3.1.0
const store = createStore( // 其实就是返回了一个特殊的store,是store.dispatch支持函数作为参数了
reducer,
applyMiddleware(thunk)
);
- redux-promise:方案二,使参数支持 “Promise对象”
让 Action Creator 返回一个 Promise 对象,乃另一种异步操作的解决方案 through 使用redux-promise
中间件。
import { createStore, applyMiddleware } from 'redux';
import promiseMiddleware from 'redux-promise';
import reducer from './reducers'; const store = createStore(
reducer,
applyMiddleware(promiseMiddleware)
);
详情请见:[JS] ECMAScript 6 - Async : compare with c#
所谓
Promise
,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。
接受 Promise 对象作为参数,有两种写法:
** 写法一,返回值是一个 Promise 对象。
const fetchPosts =
(dispatch, postTitle) => new Promise(function (resolve, reject) {
dispatch(requestPosts(postTitle));
return fetch(`/some/API/${postTitle}.json`)
.then(response => {
type: 'FETCH_POSTS',
payload: response.json()
});
});
** 写法二,Action 对象的payload
属性是一个 Promise 对象。
import { createAction } from 'redux-actions'; class AsyncApp extends Component {
componentDidMount() {
const { dispatch, selectedPost } = this.props
// 发出同步 Action
dispatch(requestPosts(selectedPost));
// 发出异步 Action, 只有等到操作结束,这个 Action 才会实际发出;
// 注意,createAction
的第二个参数必须是一个 Promise 对象。
dispatch(createAction(
'FETCH_POSTS', // 第一个参数
fetch(`/some/API/${postTitle}.json`) // 第二个参数
.then(response => response.json())
));
}
代码举例子:payload
属性是一个 Promise 对象
----> Without promise, 发送完信号后,还要听过axios.then...catch...定义”返回状态处理“函数。
----> With promise, 如下使用axios (基于promise的http库)。
import { applyMiddleware, createStore } from "redux";
import axios from "axios";
import logger from "redux-logger";
import thunk from "redux-thunk";
import promise from "redux-promise-middleware"; const initialState = {
fetching: false,
fetched: false,
users: [],
error: null,
};
// 改变state的部分value
const reducer = (state=initialState, action) => {
switch (action.type) {
/**
* 因为使用了promise,所以默认使用promise的性质
* 其中包括了promise的三个状态定义:pending, rejected, fulfilled
*/
case "FETCH_USERS_PENDING": {
return {...state, fetching: true}
break;
}
case "FETCH_USERS_REJECTED": {
return {...state, fetching: false, error: action.payload}
break;
}
case "FETCH_USERS_FULFILLED": {
return {
...state,
fetching: false,
fetched: true,
users: action.payload,
}
break;
}
}
return state
} const middleware = applyMiddleware(promise(), thunk, logger())
const store = createStore(reducer, middleware)
// 因为promise,这里就省去了
store.dispatch({
type: "FETCH_USERS", # 自定发送”添加promose默认后缀“后的信号(action)
payload: axios.get("http://rest.learncode.academy/api/wstern/users")
})
[React] 12 - Redux: async & middleware的更多相关文章
- [React] 14 - Redux: Redux Saga
Ref: Build Real App with React #14: Redux Saga Ref: 聊一聊 redux 异步流之 redux-saga [入门] Ref: 从redux-thun ...
- react脚手架改造(react/react-router/redux/eslint/karam/immutable/es6/webpack/Redux DevTools)
公司突然组织需要重新搭建一个基于node的论坛系统,前端采用react,上网找了一些脚手架,或多或少不能满足自己的需求,最终在基于YeoMan的react脚手架generator-react-webp ...
- 深入理解React、Redux
深入理解React.ReduReact+Redux非常精炼,良好运用将发挥出极强劲的生产力.但最大的挑战来自于函数式编程(FP)范式.在工程化过程中,架构(顶层)设计将是一个巨大的挑战.要不然做出来的 ...
- 基于react+react-router+redux+socket.io+koa开发一个聊天室
最近练手开发了一个项目,是一个聊天室应用.项目虽不大,但是使用到了react, react-router, redux, socket.io,后端开发使用了koa,算是一个比较综合性的案例,很多概念和 ...
- [React] 15 - Redux: practice IM
本篇属于私人笔记. client 引导部分 一.assets: 音频,图片,字体 ├── assets │ ├── audios │ ├── fonts │ └── images 二.main&quo ...
- React 与 Redux 在生产环境中的实践总结
React 与 Redux 在生产环境中的实践总结 前段时间使用 React 与 Redux 重构了我们360netlab 的 开放数据平台.现将其中一些技术实践经验总结如下: Universal 渲 ...
- immutable.js 在React、Redux中的实践以及常用API简介
immutable.js 在React.Redux中的实践以及常用API简介 学习下 这个immutable Data 是什么鬼,有什么优点,好处等等 mark : https://yq.aliyu ...
- react native redux 草稿
Provider > Provider > 使组件层级中的 方法都能够获得 Redux store.正常情况下,你的根组件应该嵌套在 Provider 中才能使用 方法. 如果你真的不想把 ...
- 实例讲解react+react-router+redux
前言 总括: 本文采用react+redux+react-router+less+es6+webpack,以实现一个简易备忘录(todolist)为例尽可能全面的讲述使用react全家桶实现一个完整应 ...
随机推荐
- [__NSArrayM insertObject:atIndex:]: object cannot be nil'
错误描述:如下图 分析原因: 1.插入的对象为空了 2.[__NSSetM addObject:] object cannot be nil [__NSArrayM insertObject:atIn ...
- grep搜索文本
正则匹配: grep -E "[a-z]+" 只输出匹配到的文本: echo this is a line. | grep -o -E "[a-z]+\." 统 ...
- 让.Net程序支持命令行启动
很多时候,我们需要让程序支持命令行启动,这个时候则需要一个命令行解析器,由于.Net BCL并没有内置命令行解析库,因此需要我们自己实现一个.对于简单的参数来说,自己写一个字符串比较函数来分析args ...
- Markdown 语法手册 - 完整版(下)
6. 引用 语法说明: 引用需要在被引用的文本前加上>符号. 代码: > 这是一个有两段文字的引用, > 无意义的占行文字1. > 无意义的占行文字2. > > 无 ...
- Chart:Amcharts
ylbtech-Chart:Amcharts Amcharts ,是一个致力于图表组件开发的公司,公司地址在立陶宛首都维尔纽斯,2004年开始推出图表和地图组件. 1. 简介返回顶部 截至目前,amC ...
- ftrace 示例
假设debugfs已经挂载到了/sys/kernel/debug目录下,下面的小脚本用来抓取unlink系统调用的耗时: cd /sys/kernel/debug/tracing echo funct ...
- 20151028整理罗列某种开发所包括对技术(技术栈),“较为全面”地表述各种技术大系的图表:系统开发技术栈图、Web前端技术栈图、数据库技术栈图、.NET技术栈图
———————————— 我的软件开发生涯 (10年开发经验总结和爆栈人生) 爆栈人生 现在流行说全栈.每种开发都有其相关的技术.您是否觉得难以罗列某种开发所包括对技术(技术栈)呢? 您是否想过: ...
- Linux 系统实时监控的瑞士军刀 —— Glances
Linux 系统实时监控的瑞士军刀 —— Glances 对于 RHEL/CentOS/Fedora 发行版 ## RHEL/CentOS 7 64-Bit ## # wget http://dl.f ...
- [Aaronyang] 写给自己的WPF4.5 笔记9[复杂数据处理三步曲,数据展示ListView泪奔2/3]
我的文章一定要做到对读者负责,否则就是失败的文章 --------- www.ayjs.net aaronyang技术分享 作者留言: 小小的推荐,作者的肯定,读者的支持. ...
- 如何用javac 和java 编译运行整个Java工程
转自:http://blog.csdn.net/huagong_adu/article/details/6929817 前言:本文教你怎么用javac和Java命令,以及如何利用脚本(she ...