Action 发出以后,Reducer 立即算出 State,这叫做同步;Action 发出以后,过一段时间再执行 Reducer,这就是异步.
怎么才能 Reducer 在异步操作结束后自动执行呢?这就要用到新的工具:中间件;(xxxMiddleware)
redux-thunk 是一个比较流行的 redux 异步 action 中间件;
源码
(1)Reducer:纯函数,只承担计算 State 的功能,不合适承担其他功能,也承担不了,因为理论上,纯函数不能进行读写操作。
(2)View:与 State 一一对应,可以看作 State 的视觉层,也不合适承担其他功能。
(3)Action:存放数据的对象,即消息的载体,只能被别人操作,自己不能进行任何操作。
以上都不能添加,所以只有 发送 action 的这个步骤可以,也就是 dispatch(action)方法 。可以添加功能;(也就是在发送对应的action中实现异步action)
中间件就是一个函数,对 dispatch() 方法 进行了改造,在发出 Action 和执行 Reducer 这两步之间,添加了其他功能。
npm i redux-thunk -S
import {applyMiddleware} from 'redux';
//接收的名字有意义就行
import thunk from 'redux-thunk'
createStore(reducer,applyMiddleware(thunk))
还是利用上一篇的 Redux的基本使用 的这个例子;
在store文件夹的 index.js文件 中,因为要在创建store仓储实例的使用引入中间键;
这里的compose到下边再说
import { createStore, applyMiddleware, compose } from 'redux';
// 这个reducer是聚合了所有的reducer的一个reducer
import reducer from '../reducers/index.js';
// 实现异步操作,利用这个中间键
import thunk from 'redux-thunk'
export default function Store(initState) {
// 创建store的时候,还有第三个参数,用来启动第三方redux的插件工具
//applyMiddleware(thunk) 用来激活redux-thunk
return createStore(reducer, initState, compose(applyMiddleware(thunk),window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()))
}
添加异步功能是要在dispatch(action) 的action中实现异步action的
此例子是在actions文件夹下的 user.js文件 中,实现异步action
import * as userConstants from '../constants/user.js'
const loginRequest = () => {
// 模拟登陆请求,发送一个登陆请求,返回一个promise对象,resolute的时候把参数传进去,也就是登陆成功以后的结果
return new Promise((resolve) => {
window.setTimeout(() => {
// 这个dispatch就是我们登陆完以后,要干什么,跟按钮点击事件中的dispatch是一样的
// 2秒之后,然后再发送一个dispatch(update),这里的update是一个同步的action
resolve({
isLogin: true,
nickname: "用户昵称",
avatar: "http://biadu.com"
})
}, 2000)
})
}
// 这里的action 接受一个数据参数,叫data,名字无所谓
export const login = (data) => {
// 异步的action
return async (dispatch) => {
// 这个是通知视图中,我们的异步正在操作,让它loading加载中,不能再点击按钮
dispatch(update({
loading: true
}))
// 这个是拿到数据以后,再去dispatch(update)
const res = await loginRequest()
dispatch(update({
...res,
// 拿到数据以后,再把loading状态取消
loading: false
}))
}
}
注: 这个文件后边的代码,在 Redux的基本使用 中;
此例子是 用户登录状态 ,所以当用户点击按钮的时候,提示用户,正在登录…,2秒后便执行 dispatch(update) 函数更新数据,
在 创建 store仓储实例的时候,传初始化默认数据的时候给一个 isloading:false 状态,
在src目录下的 index.js 文件中
const store = Store({
user: {
isLogin: false,
loading: false
},
city: {
"029": "北京"
}
})
在是视图层显示loading状态;
在App.js中添加
class App extends Component {
render() {
console.log(this.props)
const { user } = this.props
return (
<div className="App">
用户状态↓
<div>
{user.isLogin ? '欢迎你' : '未登录'}
</div>
<div>
{user.loading ? "正在加载中...." : ""}
</div>
{/* 通过触发 login这个action,进行数据的更新,并且再多传一下两个参数,点击按钮,用户状态将变成,已登录,因为login所对应的reducer数据处理,isLogin:true */}
<button onClick={() => {
this.props.dispatch(userActions.login({ account: "test", password: "123" }))
}}>登录</button>
</div>
);
}
}
因为 redux-thunk 和 redux-devtools 都是第三方的插件,createStore() 传参,不能单独传,所以只能借助于 compose()函数; 如果还有第三方的东西,依次往后添加就行了!!!
createStore(reducer, initState, compose(applyMiddleware(thunk),window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()))