Redux API之applyMiddleware
applyMiddleware(...middlewares)
使用包含自定义功能的 middleware 来扩展 Redux 是一种推荐的方式。Middleware 可以让你包装 store 的dispatch
方法来达到你想要的目的。同时, middleware 还拥有“可组合”这一关键特性。多个 middleware 可以被组合到一起使用,形成 middleware 链。其中,每个 middleware 都不需要关心链中它前后的 middleware 的任何信息。
Middleware 最常见的使用场景是无需引用大量代码或依赖类似 Rx 的第三方库实现异步 actions。这种方式可以让你像 dispatch 一般的 actions 那样 dispatch 异步 actions。
例如,redux-thunk 支持 dispatch function,以此让 action creator 控制反转。被 dispatch 的 function 会接收 dispatch
作为参数,并且可以异步调用它。这类的 function 就称为 thunk。另一个 middleware 的示例是 redux-promise。它支持 dispatch 一个异步的 Promise action,并且在 Promise resolve 后可以 dispatch 一个普通的 action。
Middleware 并不需要和 createStore
绑在一起使用,也不是 Redux 架构的基础组成部分,但它带来的益处让我们认为有必要在 Redux 核心中包含对它的支持。因此,虽然不同的 middleware 可能在易用性和用法上有所不同,它仍被作为扩展 dispatch
的唯一标准的方式。
参数
...middlewares
(arguments): 遵循 Redux middleware API 的函数。每个 middleware 接受Store
的dispatch
和getState
函数作为命名参数,并返回一个函数。该函数会被传入 被称为next
的下一个 middleware 的 dispatch 方法,并返回一个接收 action 的新函数,这个函数可以直接调用next(action)
,或者在其他需要的时刻调用,甚至根本不去调用它。调用链中最后一个 middleware 会接受真实的 store 的dispatch
方法作为next
参数,并借此结束调用链。所以,middleware 的函数签名是({ getState, dispatch }) => next => action
。
返回值
(Function) 一个应用了 middleware 后的 store enhancer。这个 store enhancer 就是一个函数,并且需要应用到 createStore
。它会返回一个应用了 middleware 的新的 createStore
。
示例: 自定义 Logger Middleware
import { createStore, applyMiddleware } from 'redux'
import todos from './reducers' function logger({ getState }) {
return (next) => (action) => {
console.log('will dispatch', action) // 调用 middleware 链中下一个 middleware 的 dispatch。
let returnValue = next(action) console.log('state after dispatch', getState()) // 一般会是 action 本身,除非
// 后面的 middleware 修改了它。
return returnValue
}
} let createStoreWithMiddleware = applyMiddleware(logger)(createStore)
let store = createStoreWithMiddleware(todos, [ 'Use Redux' ]) store.dispatch({
type: 'ADD_TODO',
text: 'Understand the middleware'
})
// (将打印如下信息:)
// will dispatch: { type: 'ADD_TODO', text: 'Understand the middleware' }
// state after dispatch: [ 'Use Redux', 'Understand the middleware' ]
示例: 使用 Thunk Middleware 来做异步 Action
import { createStore, combineReducers, applyMiddleware } from 'redux'
import thunk from 'redux-thunk'
import * as reducers from './reducers' // 调用 applyMiddleware,使用 middleware 增强 createStore:
let createStoreWithMiddleware = applyMiddleware(thunk)(createStore) // 像原生 createStore 一样使用。
let reducer = combineReducers(reducers)
let store = createStoreWithMiddleware(reducer) function fetchSecretSauce() {
return fetch('https://www.google.com/search?q=secret+sauce')
} // 这些是你已熟悉的普通 action creator。
// 它们返回的 action 不需要任何 middleware 就能被 dispatch。
// 但是,他们只表达「事实」,并不表达「异步数据流」 function makeASandwich(forPerson, secretSauce) {
return {
type: 'MAKE_SANDWICH',
forPerson,
secretSauce
}
} function apologize(fromPerson, toPerson, error) {
return {
type: 'APOLOGIZE',
fromPerson,
toPerson,
error
}
} function withdrawMoney(amount) {
return {
type: 'WITHDRAW',
amount
}
} // 即使不使用 middleware,你也可以 dispatch action:
store.dispatch(withdrawMoney(100)) // 但是怎样处理异步 action 呢,
// 比如 API 调用,或者是路由跳转? // 来看一下 thunk。
// Thunk 就是一个返回函数的函数。
// 下面就是一个 thunk。 function makeASandwichWithSecretSauce(forPerson) { // 控制反转!
// 返回一个接收 `dispatch` 的函数。
// Thunk middleware 知道如何把异步的 thunk action 转为普通 action。 return function (dispatch) {
return fetchSecretSauce().then(
sauce => dispatch(makeASandwich(forPerson, sauce)),
error => dispatch(apologize('The Sandwich Shop', forPerson, error))
)
}
} // Thunk middleware 可以让我们像 dispatch 普通 action
// 一样 dispatch 异步的 thunk action。 store.dispatch(
makeASandwichWithSecretSauce('Me')
) // 它甚至负责回传 thunk 被 dispatch 后返回的值,
// 所以可以继续串连 Promise,调用它的 .then() 方法。 store.dispatch(
makeASandwichWithSecretSauce('My wife')
).then(() => {
console.log('Done!')
}) // 实际上,可以写一个 dispatch 其它 action creator 里
// 普通 action 和异步 action 的 action creator,
// 而且可以使用 Promise 来控制数据流。 function makeSandwichesForEverybody() {
return function (dispatch, getState) {
if (!getState().sandwiches.isShopOpen) { // 返回 Promise 并不是必须的,但这是一个很好的约定,
// 为了让调用者能够在异步的 dispatch 结果上直接调用 .then() 方法。 return Promise.resolve()
} // 可以 dispatch 普通 action 对象和其它 thunk,
// 这样我们就可以在一个数据流中组合多个异步 action。 return dispatch(
makeASandwichWithSecretSauce('My Grandma')
).then(() =>
Promise.all([
dispatch(makeASandwichWithSecretSauce('Me')),
dispatch(makeASandwichWithSecretSauce('My wife'))
])
).then(() =>
dispatch(makeASandwichWithSecretSauce('Our kids'))
).then(() =>
dispatch(getState().myMoney > 42 ?
withdrawMoney(42) :
apologize('Me', 'The Sandwich Shop')
)
)
}
} // 这在服务端渲染时很有用,因为我可以等到数据
// 准备好后,同步的渲染应用。 import { renderToString } from 'react-dom/server' store.dispatch(
makeSandwichesForEverybody()
).then(() =>
response.send(renderToString(<MyApp store={store} />))
) // 也可以在任何导致组件的 props 变化的时刻
// dispatch 一个异步 thunk action。 import { connect } from 'react-redux'
import { Component } from 'react' class SandwichShop extends Component {
componentDidMount() {
this.props.dispatch(
makeASandwichWithSecretSauce(this.props.forPerson)
)
} componentWillReceiveProps(nextProps) {
if (nextProps.forPerson !== this.props.forPerson) {
this.props.dispatch(
makeASandwichWithSecretSauce(nextProps.forPerson)
)
}
} render() {
return <p>{this.props.sandwiches.join('mustard')}</p>
}
} export default connect(
state => ({
sandwiches: state.sandwiches
})
)(SandwichShop)
小贴士
Middleware 只是包装了 store 的
dispatch
方法。技术上讲,任何 middleware 能做的事情,都可能通过手动包装dispatch
调用来实现,但是放在同一个地方统一管理会使整个项目的扩展变的容易得多。如果除了
applyMiddleware
,你还用了其它 store enhancer,一定要把applyMiddleware
放到组合链的前面,因为 middleware 可能会包含异步操作。比如,它应该在 redux-devtools 前面,否则 DevTools 就看不到 Promise middleware 里 dispatch 的 action 了。如果你想有条件地使用 middleware,记住只 import 需要的部分:
let middleware = [ a, b ]
if (process.env.NODE_ENV !== 'production') {
let c = require('some-debug-middleware')
let d = require('another-debug-middleware')
middleware = [ ...middleware, c, d ]
}
const createStoreWithMiddleware = applyMiddleware(...middleware)(createStore)
这样做有利于打包时去掉不需要的模块,减小打包文件大小。
有想过
applyMiddleware
本质是什么吗?它肯定是比 middleware 还强大的扩展机制。实际上,applyMiddleware
只是被称为 Redux 最强大的扩展机制的 store enhancers 中的一个范例而已。你不太可能需要实现自己的 store enhancer。另一个 store enhancer 示例是 redux-devtools。Middleware 并没有 store enhancer 强大,但开发起来却是更容易的。Middleware 听起来比实际难一些。真正理解 middleware 的唯一办法是了解现有的 middleware 是如何工作的,并尝试自己实现。需要的功能可能错综复杂,但是你会发现大部分 middleware 实际上很小,只有 10 行左右,是通过对它们的组合使用来达到最终的目的。
Redux API之applyMiddleware的更多相关文章
- React深入 - 手写redux api
简介: 手写实现redux基础api createStore( )和store相关方法 api回顾: createStore(reducer, [preloadedState], enhancer) ...
- Redux API
Redux API Redux的API非常少.Redux定义了一系列的约定(contract),同时提供少量辅助函数来把这些约定整合到一起. Redux只关心如何管理state.在实际的项目中 ...
- Redux API之Store
Store Store 就是用来维持应用所有的 state 树 的一个对象. 改变 store 内 state 的惟一途径是对它 dispatch 一个action. Store 不是类.它只是有几个 ...
- Redux API之compose
compose(...functions) 从右到左来组合多个函数. 这是函数式编程中的方法,为了方便,被放到了 Redux 里. 当需要把多个 store 增强器 依次执行的时候,需要用到它. 参数 ...
- Redux API之bindActionCreators
bindActionCreators(actionCreators,dispatch) 把 action creators 转成拥有同名 keys 的对象,但使用 dispatch 把每个 actio ...
- Redux API之combineReducers
combineReducers(reducers) 随着应用变得复杂,需要对 reducer 函数 进行拆分,拆分后的每一块独立负责管理 state 的一部分. combineReducers 辅助函 ...
- Redux API之creatStore
createStore(reducer, [initialState]) 创建一个 Redux store 来以存放应用中所有的 state.应用中应有且仅有一个 store. 参数 reducer ...
- Redux和React-Redux的实现(三):中间件的原理和applyMiddleware、Thunk的实现
现在我们的Redux和React-Redux已经基本实现了,在Redux中,触发一个action,reducer立即就能算出相应的state,如果我要过一会才让reducer计算state呢怎么办?也 ...
- Redux基础
Redux 是一个状态容器 Redux 就像是作者自己的介绍,它不会为你提供任何的东西,它不会告诉你如何做路由,它只专注于应用程序状态,是一个 JavasSript 的状态容器,所有的状态的变化都是当 ...
随机推荐
- JS实现图片无缝滚动特效;附addEventListener()方法、offsetLeft和offsetWidth属性。
一:html部分 <body> <input id="btn1" type="button" value="向左"> ...
- Vue知识随记
数据绑定内支持JavaScript表达式:string字符串反转用.隔开 js: msg:'Hello ' html: {{ msg.split('').reverse().join('.') }} ...
- 我的Java开发学习之旅------>System.nanoTime与System.currentTimeMillis的区别
首先来看一道题:下面代码的输出结果是什么? import java.util.HashMap; import java.util.Map; public class HashMapTest { pub ...
- 我的Android进阶之旅------>直接拿来用!最火的Android开源项目
转载于CSDN,相关链接如下: http://www.csdn.net/article/2013-05-03/2815127-Android-open-source-projects http://w ...
- 链表的C++实现
有的时候,处于内存中的数据并非连续的.那么这时候.我们就须要在数据结构中加入一个属性.这个属性会记录以下一个数据的地址.有了这个地址之后.全部的数据就像一条链子一样串起来了,那么这个地址属性就起到 ...
- HTML5_CSS3可切换注册登录表单
在线演示 本地下载
- SETEVENT的使用
来源:https://msdn.microsoft.com/en-us/library/windows/desktop/ms686915(v=vs.85).aspx 昨天看到这个SetEvent的方法 ...
- Nginx之解压编译安装-yellowcong
安装前准备 对于nginx编译安装需要先安装编译 的工具,然后再安装nginx依赖 yum -y install gcc gcc-c++ autoconf automake make yum -y i ...
- nginx+keepalived简单双机主从热备
双机主从热备概述 可以两台机子互为热备,平时各自负责各自的服务.在做上线更新的时候,关闭一台服务器的tomcat后,nginx自动把流量切换到另外一台服务的后备机子上,从而实现无痛更新,保持服务的持续 ...
- OpenGL几何变换---翻译http://www.songho.ca/opengl/gl_projectionmatrix.html
Overview 几何数据——顶点位置,和法向量(normal vectors),在OpenGL 管道raterization 处理过程之前可通过顶点运算(Vertex Operation)和基本组合 ...