简介
如果想看这几个家伙的详细用法,请自行翻阅这仨的官方文档,我只梳理他们出现的原因以及基本概念。
预备知识
一定要理解Flux单向数据流:

Redux
这个家伙是Flux概念最好的实现之一。我们按照刚才Flux的思想,来看看Flux是怎么实现其中的概念的:
Store
Store在redux里也被定义为Store,他是唯一的数据中心,如何创建Store:
import { createStore } from 'redux';
// 具体参数我们后面再看
const store = createStore(...params);
Action
redux将其封装成一个对象:
const action = {
type: 'ADD',
payload: 1,
};
Dispatcher
Store实例下有一个dispatch方法,用来派发我们的每一个Action,这也是View触发Action的唯一方式:
store.dispatch(action)
Reducer
通过store.dispatch我们的Store已经接收到了Action,那么之后需要根据Action.type和Action.payload修改Store里的数据,这部分redux提供Reducer来实现这部分操作。他和JS原生的reduce方法一样,接受一个当前state的,返回一个最新的state:
// 接受当前的state和传入的action作为参数
// 默认的state为0,定义默认state的更常用的方法后面我们会介绍
function counterReducer(state = 0, action) {
switch (action.type) {
case 'ADD':
return state + action.payload;
case 'MINUS':
return state - action.payload;
default:
return state;
}
}
store.dispatch派发了action之后,我们肯定希望直接就调用reducer,那么怎么将store和action关联?这就是我们createStore的第一个参数:
const store = createStore(counterReducer);
关联之后,action触发之后就走到了counterReducer里,执行我们对数据的操作。
前面说过了,我们的Store是惟一的数据中心,那么一个项目中,这个Store恐怕是非常大的,那我们的Reducer也会变得非常大,Redux为了解决这个痛点提供了combineReducers方法,详细用法请查看Redux官方文档
subscribe
当我们Store中的数据发生了改变之后,如何通知到View发生变化呢?Redux提供了Store的subscribe方法,用来搭建Store和View之间桥梁:
store.subscribe(listener)
这样,当我们的store中的数据发生改变之后,就会立即通知listener发生改变,在react项目中,这个listener一般就是我们的render方法
createStore方法
为什么要把我们一开始初始Store的方法放到现在来说呢?因为这个方法里面的参数是在你了解了Redux是如何实现Flux之后理解起来才比较容易的。
createStore接受三个参数:第一个参数就是我们的reducer,第二个参数是我们Store的初始状态,如果设置了他,那么我们reducer中第一个参数默认的初始状态会失效,第三个参数用来处理中间件的
中间件
中间件是Action发起之后,Store接收到之前的一系列扩展方法,使用过中间件之后我们的流程变为:

举个例子🌰:比如我们的处理Action时很可能要进行一些发起请求之类的异步操作,那我们就要用到redux-trunk或者redux-promise这些中间件,具体用法请参考他们的官网。(不过我还是推荐react-saga,详情看后文)
至此,redux的基本概念已经明了了,我们继续看react-redux干了啥
React-Redux
简介
React官方为Redux提供的扩展库,让我们很嗨皮的在项目中使用Redux。
主要功能是connect方法和Provider组件。
Provider组件
在React中Context的基础上实现,如果不了解,请先了解React中的Context部分。
我们的React项目一般数据都是自上而下传递的,那么我们如果在根组件上绑定了store,孙子组件们都能拿到store的数据。Provider存在的价值就是我们的孙子组件能更方便的获取到根组件的store。
connect
接受四个参数:
- mapStateToProps:是一个function,字面意思很清楚,就是将Redux中的数据映射到Component的props上,达到Store和View绑定的目的(这不就是前面的subscribe做的吗)。本身参数有两个,一个是Store中的数据,第二个参数是组件本身的props。 mapStateToProps返回一个对象,表明state和要绑定Component的props的映射关系。
const mapStateToProps = state => ({
foo: state.bar
})
const Foo = ({foo}) => {
// 这样我们的props里的foo其实是state里的bar
return <div>{foo.bar}</div>
}
export default connect(mapStateToProps)(Foo);
- mapDispatchToProps,和第一个参数雷同,只不过是把dispatch和props关联,使我们的组件可以通过调用Props上的方法来触发dispatch
const mapDispatchToProps = dispatch => ({
add: () => {dispatch({type: 'ADD'})}
})
const Foo = ({add}) => {
// 这样我们的props里的foo其实是state里的bar
return <div onClick={add}>Click Me</div>
}
export default connect(mapStateToProps, mapDispatchToProps)(Foo);
- 后两个参数用的很少,可以查阅官方文档,不做赘述。
Redux-saga
这是目前处理Redux异步Action的中间件中,我认为最为优雅的方案。
首先我们在概念上把这些网络请求,读取等异步操作统称为副作用(Side Effects),并不意味着不好,这完全取决于特定的编程范式。
saga是一个Generator函数,如果不清楚,推荐先去看一下这篇文章
使用redux-saga后我们的数据流变成了:

takeEvery
// rootSaga.js
import { takeEvery } from 'redux-saga'
import { call, put } from 'redux-saga/effects'
const timeout = time => new Promise(resolve => setTimeout(resolve, time));
function* addAsync() {
yield call(timeout, 2000);
yield put({ type: 'ADD' });
}
export default function* rootSaga() {
yield* takeEvery("ADD_ASYNC", addAsync)
}
yield call 和 yield put 我们一会儿再看,先解释一下takeEvery,这个函数监听一个action的type,也就是说一旦发现触发了ADD_ASYNC的action,那么就会立即执行addAsync方法
takeLatest
在实际需求中,我们可能只需要拿到最后一次执行的结果,这就用到takeLatest。简单的说,如果我们使用takeEvery,每次ADD_ASYNC触发时都会调用addAsync,而如果是takeLatest,如果ADD_ASYNC连续被触发,那么如果上一次触发未结束,会先结束上一次触发,执行最新的这次触发。
示例和takeEvery相似,不做赘述。
Effect 方法
最常用的是 put 和 call
- put 可以简单的理解为Redux的dispatch,阻塞后面的逻辑
// 触发一个type为ADD的action,Reducer会重新计算state并返回
yield put({
type: 'ADD',
payload: 1
})
- call 调用其他函数,可以是一个Generate函数,也可以是返回一个Promise的普通函数。阻塞后面的逻辑
const timeout = time => new Promise(resolve => setTimeout(resolve, time));
function* addAsync() {
yield call(timeout, 2000);
}
Dva
看到这里,是不是觉得很繁琐,我写一个项目,怎么要引这么多库?别愁,Dva来了
Dva是Redux和Redux-saga之上的一个框架,就像Egg对于Koa一样,都是框架之上的另一层封装,我们来看一下他是如何解决上面的痛点的
Model
dva 通过 model 的概念把一个领域的模型管理起来,包含同步更新 state 的 reducers,处理异步逻辑的 effects,订阅数据源的 subscriptions
// addModel.js
{
namespace: 'addModel',
state: 0,
reducers: {
add(state) { return state + 1 },
},
effects: {
*addAfter1Second(action, { delay, put }) {
yield delay(1000);
yield put({ type: 'add' });
},
},
}
然后我们再把Model和Component通过connect连接起来就ok了,connect用法参考前面的react-redux的connect方法。至此我们之前所说的Redux和Redux-Saga都集成在了model中,真的是棒棒。
友情提示,这两个搭配使用效果更佳哦:
Umi
Dva

未经允许,禁止转载