demo 地址:
https://github.com/chris6605/react-native-redux
1 >> 什么是redux ? 你的项目需不需要redux?
有个大神说过? 如果你觉得你的项目不需要redux, 那么你就真的不需要去用 redux.
redux 到底是什么呢? 其实就是用来统一管理整个应用 State 的, 将所有的 state 变化进行统一的流程处理, 使应用的 state变化清晰可见.
如图 : 使用redux和不使用组件之间的通信方式的不同
在React中,数据在组件中是通过 props 单向流动的。数据从父组件流向子组件,由于这个特征,两个兄弟组件之间的通信并不是那么清楚。
React并不建议直接采用组件到组件的通信方式,需要提一点的是, 在使用 redux 之后你就不能直接调用组件的方法了, 比如你写个弹窗有个 show() 方法, 你在使用的地方通过引用 this.xxxDialog.show()是不会起作用的, 需要严格遵守单向数据流的思想. 尽管它有一些特性可以支持这么做(比如先将子组件的值传递给父组件,然后再由父组件在分发给指定的子组件)。这被很多人认为是糟糕的实践方式,因为这样的方式容易出错而且会让代码向“拉面”一样不容易理解。
当然React也没有直接建议如何去处理这种情形, React 官方是这么解释的:
对于非父子关系的组件,你可以自己建立一个全局的事件系统,Flux的模式也是一种可行的方式。
Redux的出现就让这个问题的解决变得更加方便了。
Redux提供一种存储整个应用状态到一个地方的解决方案(可以理解为统一状态层),这个存储所有应用状态的地方称为“store”,组件发生事件时不会再 setState, 而是由 store 分发(dispatch)一个事件(action), 组件将状态的变化通知给store,而不是直接通知其它的组件, store会根据 action 的类型来调用对应的 reducer(纯函数, 告诉 state 该做出什么变化), reducer 会根据 action 的类型, 接收一个旧的 state, 返回一个新的 state, store 会收集到所有 reducer的state, 最后更新 state, 组件内部依赖的state的变化情况就可以通过订阅 store 来实现。如图所示:
使用Redux,所有的组件都从store里面获取它们依赖的state,同时也需要将state的变化告知store。组件不需要关注在这个store里面其它组件的state的变化情况,Redux让数据流变得更加简单。这种思想最初来自Flux,它是一种和React相同的单向数据流的设计模式。
如果你的页面单一页面, 功能简单, 状态不多变, 没有涉及多个页面之间的交互, 那么你就不用往下看了, 你真的不需要用 redux这么重量级的东西, 但是比如做多语言, 换肤功能, 字体切换也可以考虑用redux.
redux 的三原则
1. Single source of truth
单一数据源。整个应用的state,存储在唯一一个object中, 那就是 store, 一个应用有且只有一个 store
2. State is read-only
状态是只读的。唯一能改变state的方法,就是触发action操作。不要想着再去 setState 了, 用了 redux 之后你改变 state 唯一的方法就是 dispatch 一个 action, action是什么? 别急,下面再说
3. Changes are made with pure functions
在改变state tree时,用到action,同时也需要编写对应的reducers才能完成state改变操作。并且 reducer 一定要是一个纯函数, 不要在里面做一些乱七八糟的骚操作
2 >> 什么是 Action? reducer ? store ? provider?
Action 动作 :比如登录操作 退出登录操作 添加待办 删除待办都可以定义为一个 action
example:
这就是一个完整的 store 的创建过程, createStore 是redux 里提供的函数, 接受初始状态, reducer( reducer 可以有多个, 这里是合并多个 reducer 的 RootReducer ), 中间件.
store 有什么用呢? store 就是一个保存着整个 APP 的所有state 的对象, 顾名思义, 就是一个仓库嘛, 在任何一个组件中都可以通过store 来获取任何一个 state,
Reducer 是一个纯函数, 不要在这里做逻辑操作的事情, 他就是根据一个 action 的 type 返回一个新的 state 一定要保证其纯洁性
异步Action
// 异步的Action 需要返回的是一个 function 在 createStore 时由middleWare 做处理 普通的action 直接返回纯对象即可 | |
export function requireLogin(info) { | |
return LoginApi(info); | |
} |
这其实就是一个 action(只不过是个异步的 action , 如下图上面是一个普通的同步 action , dispatch 这个 action 马上就执行, 返回新的 state, UI 更新, 但是异步的不同, 不能马上获取新的 state
这里模仿请求的百度, 请求完成登录成功, 异步的 action 返回的不是纯对象了, 而是一个函数, 就是上图那个函数, 另外在创建 store 的时候还要使用middleware 中间件, 这样才能执行异步操作)
废话都说的差不多了, 相信你对 redux大概有个了解了, 起码对 action reducer store 这三个概念有个印象, action 就是描述一件事情发生的对象, reducer 就是个纯函数, 接受旧的 state 和一个 action, 返回一个新的 state 到 store, store 就是全局唯一的一个仓库,存储着应用所有的 state
Provider
provider主要有两个功能
- 在原应用组件上包裹一层,使原来整个应用成为Provider的子组件
- 接收Redux的store作为props,通过context对象传递给子孙组件上的connect
context
可以使子孙组件直接获取父级组件中的数据或方法,而无需一层一层通过props向下传递。context
对象相当于一个独立的空间,父组件通过getChildContext()
向该空间内写值;定义了contextTypes
验证的子孙组件可以通过this.context.xxx
,从context对象中读取xxx字段的值。
总而言之,Provider模块的功能很简单,从最外部封装了整个应用,并向connect模块传递store。
而最核心的功能在connect模块中。
connect的作用:
1、connect通过context获取Provider中的store,通过store.getState()获取整个store tree 上所有state。
2、connect模块的返回值wrapWithConnect为function。
3、wrapWithConnect返回一个ReactComponent对象Connect,Connect重新render外部传入的原组件WrappedComponent,并把connect中传入的mapStateToProps, mapDispatchToProps与组件上原有的props合并后,通过属性的方式传给WrappedComponent
下面进入实战
1. 创建你的项目, 新建如下文件夹
constants actions reducers store pages
这里说一下 不做异步请求的话 就不用像我那样用中间件 action 返回函数, 直接return 一个对象, 只有一个 type字段就OK 项目的在 Github 上自己可以看一下
2 npm install react-redux redux redux-logger(这个会打印你的所有 state和 action变化 , 建议使用) redux-thunk(中间件, 用于异步操作) react-navigation(如果你要做多个页面跳转就install)
3. 新建一个 Root.js
用 provider 包住你的 APP, 这样所有的组件就都可以获取到 store 的 state 变化了 注意要传递 store
然后就是把 Root.js 当成 App.js一样, 放到你的 index.js里即可
4. 接下来就是编写 action reducer store 了
举个简单的加减的同步操作的例子
actionCreactor: 函数返回一个 action
reducer:
这里要提一下, reducer 可以有很多个, 拆分 reducer 对一个不同的操作, 然后用combineReducers 合并所有的 reducer 到 rootReducer, 如下图:
我个人习惯叫 xxxReducer , 在组件中使用的时候, 可以 state.xxxReducer.xxx 便于区分
5. 在 page里怎么使用
example登录的操作
下面登录就这么简单了只需要一句 this.props.loginAction() , 登录的逻辑在 loginRequest 里, 发起登录操作即可, 也算可以给 page解耦,
这里要注意一下 bindActionCreactors 他的作用是把 dispatch 函数无感知的传递给子组件, 在子组件里就可以 dispatch 一个 action, 当然你也可以通过 props 把dispatch 函数传递给子组件, 只是不优雅, 该函数接受两个参数, 第一个参数可以是函数或者对象,一般是一个 actionCreactor, 返回的也是一个函数或者对象, 第二个参数是 dispatch,
这里有个 connect 函数 他的作用就是把当前组件和 store 连接, 然后就可以获取 store 里的 state, store 中的 state 在 dispatch一个 action 之后, 在 reducer 的作用下产生新的 state 返回到 store, 这时你的组件也能接收到新的 state, 不过是以 props 的形式, 如上 connect 函数接受两个参数, 第一个参数接受两个参数分别是 mapStateToProps (名字就表示了他的意思, 你需要获取的 state 会通过props 的形式传递到你的当前组件) matchDispatchToProps 这个是你需要的dispatch 的 action , 我更喜欢直接在发生事件的时候去this.props.dispatch(action), 当然这种不规范, 你们还是用matchDispatchToProps 然后就能直接 this.props.loginAction()就 OK 了
这个是那个简单的同步操作加减的, count就是那个 state, 这里已经通过 mapStateToProps 获取了, 所以使用的时候就可以this.props.count , 当你 dispatch一个 action 的时候, reducer 会改变他的值, 返回到 store , 最后通过 connect, store 会把这个改变后的 state 更新到这里
当然在其他地方也可以dispatch 这个 action, 这里的 count 也会变化, 当然在其他地方也可以获取到这个 count, 这就是他的强大之处, 任何地方都能获取到 store 中存储的 所有的state, 在任何地方都可以 dispatch 一个你想要 dispatch 的 action, 可以跨组件的操作和数据传递提供了很大的便利性和可维护性, 所以这也是他适用于逻辑复杂的应用的原因
下面附上那个 redux-logger 的日志 里面有store 里详细的 state 变化 以及发起的每一个 action 所以说对于解决 bug 调试起来很省事
是不是一目了然, 你触发了哪个 action , 改变了什么状态以及最终的状态, 传递的数据都清晰明了,
总结一下吧, redux 很强大, 也很易用, 下面是一个流程图
store 就是仓库
action 就是描述事件发生的对象
reducer 就是事件发生之后如何改变state 的一个纯函数