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 的状态容器,所有的状态的变化都是当 ...
随机推荐
- python 基础 7.6 sys 模块
一.sys 模块 sys 模块主要功能是获取参数 [root@www pythonscripts]# cat 2.py #!/usr/bin/python #coding=utf-8 im ...
- LR中select next row和update value on的设置
LR的参数的取值,和select next row和update value on的设置都有密不可分的关系.下表给出了select next row和update value on不同的设置,对于LR ...
- MessageDigest和DigestUtils加密算法
总结:使用DigestUtils的方法加密的结果与messageDigest的方法加密结果一致,可使用DigestUtils替换MessageDigest 可省掉部分代码 package com.a ...
- WCF基础之配置服务
在WCF应用编程中配置服务是其主要部分. 配置可以定义和自定义如何向客户端公开服务,包括服务地址,发送和接受消息的传输和编码,以及服务的安全类型. 服务的配置有两种:编码和使用config文件,大多数 ...
- 九度OJ 1016:火星A+B (进制转换)
时间限制:1 秒 内存限制:32 兆 特殊判题:否 提交:4913 解决:1334 题目描述: 读入两个不超过25位的火星正整数A和B,计算A+B.需要注意的是:在火星上,整数不是单一进制的, ...
- The goroutine scheduler is not preemptive.
go - Why is time.sleep required to run certain goroutines? - Stack Overflow https://stackoverflow.co ...
- css 字体的unicode码
微软雅黑: YaHei宋体: SimSun黑体: SimHei ;
- A. Playing with Paper
这是Codeforces Round #296 (Div. 2)的A题,题意就是: 小明有一张长为a,宽为b的纸,每当要折纸鹤时,就从纸上剪下一个正方形,然后,剩下的纸还可以剪出正方形,要是剩下的纸刚 ...
- Django 之 Paginator 分页功能
Django Paginator Django 分页官方文档 https://docs.djangoproject.com/en/1.10/topics/pagination/ 此分页方法没有限制显 ...
- 51Nod XOR key —— 区间最大异或值 可持久化字典树
题目链接:http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1295 1295 XOR key 题目来源: HackerRa ...