![]()
在这张图中,我们可以很清晰的看到,view中产生action,通过store.dispatch(action)将action交由reducer处理,最终根据处理的结果更新view。
在这个过程中,action是简单对象,用于描述一个动作以及对应于该动作的数据。
我们来看下redux到底是如何工作的。
<pre>
import React from 'react'
import { createStore, bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import ReactDom from 'react-dom'
import { Provider } from 'react-redux'
<b />
function createAction(){
return{
type: 'ADD_TODO',
data: 'some data'
}
}
<b />
class App extends React.Component{
constructor() {
super();
}
render() {
return(
<div style={{
width:'200px',
height:'200px',
margin:'100px',
border:'2px solid black'
}}>
<div onClick = {
this.props.actions.createAction.bind(this)
}> {"Click Me!"} </div>
</div>
) ;
}
}
<b />
function mapStateToProps(state){
return{
data: state
}
}
<b />
function mapDispatchToProps(dispatch){
return{
actions: bindActionCreators({createAction}, dispatch)
}
}
<b />
var AppApp = connect( mapStateToProps, mapDispatchToProps)(App);
<b />
function reducer(state, action){
console.log(action);
return state;
}
<b />
var store = createStore(reducer);
<b />
ReactDom.render(
<Provider store={store}>
<AppApp/>
</Provider>
, document.getElementById('container'));
</pre>
-
这是一个精简版本的redux demo,每点击一次“Click Me!”,控制台会打印一次action。
下面是截图:
效果图
-
控制台打印输出:
控制台打印
-
从上面代码中可以清晰的看出,
当用户点击“Click Me!”的时候,会立即调用createAction产生一个action,之后redux获取这个action并调用store.dispatch将这个action丢给reducer进行处理,demo中的reducer仅仅打印了action。
数据从view中流出,经reducer处理后又回到了view。
至此,我们看到的一切都是跟上面的基本认知是一致的。
Redux Middleware
-
redux为我们做了很多的事情,我们都可以不用通过显示的调用dispatch函数就将我们的action传递给reducer。
但是redux一直没有解决异步的问题。试想,如果我在页面输入一段内容,然后触发了一个搜索动作,此时需要向服务端请求数据并将返回的数据展示出来。
涉及到异步请求,刚刚的demo中的方法已经不再适用了,需要引入middleware。 -
我们看看redux-thunk的代码:
一个三目符,如果action是一个函数,执行这个action函数,如果不是函数,执行next函数。
<pre>
export default function thunkMiddleware({ dispatch, getState }) {
return next => action =>
typeof action === 'function' ? action(dispatch, getState) : next(action);
}
</pre>
-
结合middleware的应用:
<pre>
const finalCreateStore=applyMiddleware(thunkMiddleware)(createStore)
const store = finalCreateStore(reducer)
</pre> -
这就是我们最常使用middleware的代码。把源码中的next换成createStore,如果action是一个函数(这里的action是改造后的ActionCreator),便会执行这个action(dispatch, getState)函数。
<pre>
var asyncSayActionCreator = function (message) {
return function (dispatch) {
setTimeout(function () {
dispatch({ type: 'SAY', message })
}, 2000) }
}
</pre> -
这里的action是return的函数:
<pre>
function (dispatch) {
setTimeout(function () {
dispatch({ type: 'SAY', message })
}, 2000)
}
</pre> -
如果action返回的不是函数,即返回的是action对象的话,执行createStore函数的dispatch方法。
有了middleware之后,数据流动的方向变为:
action ---> dispatcher ---> middleware 1 ---> middleware 2 ---> reducers
自己的middleware怎么写?
学了这些,我们可以自己写一个middleware练练手。首先在项目下建个middlewares的文件夹,新建一个callTraceMiddleware.js来追踪函数的调用过程。在funCallTrace.js添加如下代码:
<pre>
export default function callTraceMiddleware ({dispatch,getState}){
return next=> action =>{
console.trace();
return next(action);
}
}
</pre>
然后在调用中间件部分添加中间件:
<pre>
const createStoreWithMiddleware = applyMiddleware( thunkMiddleware, loggerMiddleware, callTraceMiddleware)(createStore);
</pre>
这样我们运行在浏览器窗口就可以看到打印的函数调用轨迹。是不是很简单……
网友评论