react-native基础:redux学习1-理论篇

导航

react-native基础:redux学习1-理论篇

react-native基础:redux学习2-实战篇

理论

本篇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
2
3
4
5
6
7
8
├── utils/
│ ├── warning.js # 打酱油的,负责在控制台显示警告信息
├── applyMiddleware.js
├── bindActionCreators.js
├── combineReducers.js
├── compose.js
├── createStore.js
├── index.js # 入口文件
  • 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

指明如何更新statereducer 本质是一个一function,接收旧stateaction,返回新的 state,我们叫nextState,也就是说每次返回的都是新的state,而不是旧state的副本。reducer返回啥,state就被替换成啥。

思考:这样就能实现数据的回滚了,比如执行redo,undo操作了。

§ store

Store 就是把它们联系到一起的对象。Store 有以下职责:

  • 维持应用的 state;
  • 提供 getState() 方法获取 state;
  • 提供 dispatch(action) 方法更新 state;
  • 通过 subscribe(listener) 注册监听器。

一个Redux应用只能有一个单一的store

§ compose

主要的作用是让代码更优雅,如下:

1
2
3
4
var re1 = func3(func2(func1(0)));
//替换为
var re2 = Redux.compose(func3, func2, func1)(0);
//是不是更优雅了

§ creteStore(reducer,initalState,enhaner)

经常的用法是创建store,并给store添加中间件

1
2
3
4
5
6
7
8
9
10
11
12
const finalCreateStore = Redux.compose(
中间件1,
中间件2,
中间件3
)(createStore)

const store = finalCreateStore(reducer);
//执行流程是
//reducer作为参数传递给函数createStore,返回store
//store作为参数传递给函数中间件3,返回store
//store作为参数传递给函数中间件2,返回store
//store作为参数传递给函数中间件1,返回store

§ combineReducers(reducers)

无论应用的状态树多复杂,都可以通过逐层下分,来管理对应部分的state,如下图

1
2
3
4
5
6
7
8
                                 aReducer(a_value, action) -------------------- a_value
↗ ↘
rootReducer(state, action) —→∑ ↗ b1Reducer(b1_value, action) ------ b1_value ↘ nextState
↘—→∑ b_value ↗
↘ b2Reducer(b2_value,action) ----- b2_value ↗


注:左侧表示 dispatch 分发流,∑ 表示 combineReducers;右侧表示各实体 reducer 的返回值,最后汇总整合成 nextState

无论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 action

1
2


createAction(type, payloadCreator = Identity, ?metaCreator)

  • 参数type:就是actiontype,一般是string
  • 参数payloadCreator:就是payload生成器,类型只能是functionundefined或者null

createAction是一个Action生成器,返回一个Flux Standard Action,这个ActionpayLoad的值就是payloadCreator的返回值

举例:

1
2
3
4
5
let increment = createAction('INCREMENT', amount => amount);
expect(increment(42)).to.deep.equal({
type: 'INCREMENT',
payload: 42
});

好处是如果payload是一个Error对象(new TypeError),react-actions就能自动把Action.error = true

举例:

1
2
3
4
5
6
7
8
const increment = createAction('INCREMENT');

const error = new TypeError('not a number');
expect(increment(error)).to.deep.equal({
type: 'INCREMENT',
payload: error,
error: true
});

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 typekey,reducervalue的键值对。
  • 参数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的操作
    

参考文献

Redux 中文文档

React-Native With Redux

深入到源码:解读 redux 的设计思路与用法

这段时间研究了下Redux,写写自己对它的感觉。

Redux 简明教程