导航
理论
本篇blog主要讲解redux的一些理论,以及第三方的组件库和其优秀的解决思路,所谓理论现行。
我在查阅了大量参考文章和blog后,有感于大部分文章都侧重于讲某个点,而没有系统的让初学者从0开始对redux进行理解,所以写下了这篇总结。
当然我们首先还是要站在巨人的肩膀,需要预先阅读一下文档,我起初读完以下文档也是萌萌哒,直到对所有的知识串起来思考,才有恍然大悟的感觉。首先是Redux 中文文档,然后是深入到源码:解读 redux 的设计思路与用法,以及React-Native With Redux,最后如果对redux的基础知识有了初步了解,可以看下这个讨论帖这段时间研究了下Redux,写写自己对它的感觉。
Flex与Redux的关系
Redux是遵循了单向数据流Flex的设计,但是做了流程的简化。
Redux是javascript状态容器,提供可预测化的状态管理,可以构建一致化的应用,除了和React一起用外,还支持其他界面库,体积小(只有2kb)而且没有任何依赖。Redux由Flux演变而来,但是避开了Flux的复杂性,上手快,使用简单,而且社区活跃,是目前主流的Flux数据流框架(学习Redux可以参考Redux中文文档)
Redux总览
在 Redux 的[源码目录][redux-src]
src/
,我们可以看到如下文件结构:
1 | ├── utils/ |
- store: 应用中所有的
state
都以一个对象树的形式储存在一个单一的store中,state
就是每个component
里都存在的状态集合,你也可以理解为java类的属性和方法。 - action: 惟一改变 state 的办法是触发 action,一个描述发生什么的对象。它是 store 数据的唯一来源。
- reducers:为了描述 action 如何改变 state 树,你需要编写 reducers。
- compose:没有依赖,是个纯函数,实现函数的层级合并,从右开始。
- createStore:创建store的函数。参数是(reducer,初始化state,中间件),返回store
- combineReducers(reducers):可以合并不同的reducer,便于不同功能的reducer进行分支化管理
- bindActionCreators(actionCreators,dispacth):实现自动
dispatch
§ action
action本质是是一个包含type
属性的普通对象,type是它的key也是识别码。改变state必须dispatch
一个action
。
一般用法component的state其实是从props传递的,以propsMaptoState形式存储在store中。
高阶用法包括selector绑定
,这是为了解决全局变化而对store的state做了最小化拆分
- 存储最小的可能变化state
- 提高效率,除非一个参数发生改变,否则不会重新计算
- selector是可组合的,一个selector可以导出给其他
selector
使用
§ reducer
指明如何更新state
,reducer
本质是一个一function
,接收旧state
和 action
,返回新的 state
,我们叫nextState
,也就是说每次返回的都是新的state,而不是旧state的副本。reducer返回啥,state就被替换成啥。
思考:这样就能实现数据的回滚了,比如执行redo,undo操作了。
§ store
Store 就是把它们联系到一起的对象。Store 有以下职责:
- 维持应用的 state;
- 提供 getState() 方法获取 state;
- 提供 dispatch(action) 方法更新 state;
- 通过 subscribe(listener) 注册监听器。
一个Redux应用只能有一个单一的store
§ compose
主要的作用是让代码更优雅,如下:
1 | var re1 = func3(func2(func1(0))); |
§ creteStore(reducer,initalState,enhaner)
经常的用法是创建store,并给store添加中间件
1 | const finalCreateStore = Redux.compose( |
§ combineReducers(reducers)
无论应用的状态树多复杂,都可以通过逐层下分,来管理对应部分的state,如下图
1 | aReducer(a_value, action) -------------------- a_value |
无论dispatch哪个action,都会流通所有的reducer
这也是为何 reducer 必须返回其对应的 state 的原因。否则整合状态树时,该 reducer 对应的键值就是 undefined
§ bindActionCreators(actionCreators,diapatch)
把我们手动的action的dispatch过程变成了自动.
⊙ Redux 与传统后端 MVC 的对照
Redux | 传统后端 MVC |
---|---|
store |
数据库实例 |
state |
数据库中存储的数据 |
dispatch(action) |
用户发起请求 |
action: { type, payload } |
type 表示请求的 URL,payload 表示请求的数据 |
reducer |
路由 + 控制器(handler) |
reducer 中的 switch-case 分支 |
路由,根据 action.type 路由到对应的控制器 |
reducer 内部对 state 的处理 |
控制器对数据库进行增删改操作 |
reducer 返回 nextState |
将修改后的记录写回数据库 |
第三方辅助类组件库
§ redux-actions
专门为redux准备的,flux standard action的工具集。
什么是flux standard action?
简称fsa,1
2
3
4
5
6
7{
type: 'ADD_TODO',
payload: {
text: 'Do something.'
}
}
出错时1
2
3
4
5{
type: 'ADD_TODO',
payload: new Error(),
error: true
}
type必须的,payload可选的,error可选的,meta可选的
判断一个action是否是flux standard action1
2
createAction(type, payloadCreator = Identity, ?metaCreator)
- 参数type:就是
action
的type
,一般是string
, - 参数payloadCreator:就是
payload
生成器,类型只能是function
,undefined
,或者null
,
createAction
是一个Action生成器,返回一个Flux Standard Action
,这个Action
的payLoad
的值就是payloadCreator的返回值
举例:
1 | let increment = createAction('INCREMENT', amount => amount); |
好处是如果payload是一个Error对象(new TypeError
),react-actions
就能自动把Action.error = true
举例:
1 | const increment = createAction('INCREMENT'); |
createAction
也能返回它的type
,然后作为type
传参给handleAction
或者handleActions
使用
- 作为参数在
handleAction
中使用 - 作为object key 在handleActions中使用
handleAction(type, reducer | reducerMap = Identity, defaultState)
包装一个reducer,这个reducer仅能处理一个,确定type的、flux标准的action。
- 参数type:就是一个action的type
- 参数reducer:就是reducer函数,用来处理传递的action里的payload和state,然后返回新的state
- 参数defaultState,当reducer收到的参数是
undefined
时,就返回defaultState
handleActions(reducerMap, defaultState)
- 参数reduerMap:就是一个
action type
为key
,reducer
为value
的键值对。 - 参数defaultState:
把大量使用handleAction
创建的reducer
合并
§ Reselect
Reselect是一个第三方库,解决数据冗余的问题。这个问题是怎么产生的呢?
首先:原本store中的state是通过props,数据一级一级的传递,在 render 中处理,避免了状态判断的问题,即拿到的数据总是最新的,缺点就是每次重新计算。
然后:一层层传递的数据,每个层级组件都要用,一旦原始数据被污染了,需要保存多份原始数据的处理结果,数据的冗余就出现了。
解决方案:
Container component 模式很,就是每个组件connect自己所需要的数据.
reselect的三个特性
- Selectors can compute derived data, allowing Redux to store the minimal possible state.(Selectors可以计算派生数据,允许Redux存储最小可用state)
- Selectors are efficient. A selector is not recomputed unless one of its arguments change.(Selectors是高效的。除非其中一个参数发生更改,否则不会重新计算Selectors)
- Selectors are composable. They can be used as input to other selectors.(Selectors是可组合的,也可以作为input给其他Selectors使用)
React-Redux容器组件与展示组件分离的开发思想
这个在Redux 中文文档中有详细跟直观的讲解。
通俗来讲就是,一个Redux应用切成两部分来管理数据流
Container:容器组件
-在这里使用redux,控制分发需要向下传递的state,action创建函数
View:展示组件
-我们要展示数据的的view,以及触发action的操作