Redux 简介
本文最后更新于 2021年4月4日 晚上
简单介绍 Redux 在实践中的使用.
关于 Redux
这个链接是目录https://medium.com/@ohansemmanuel/table-of-contents-for-understanding-redux-1-ea0667e1453d
Rule1:
Have a single source of truth: The state of your whole application is stored in an object tree within a single Redux store.
这点并非实践中一定如此, 当有很多组件时, 可以将每个组件分离出来单独的一个 Store.
视图拿到的是 store 的只读版本. 通过 getState 拿到当前状态的只读版本!
和 Flux 不同, Redux 规定将 APP 中的全部状态都保存到同一个 store 中, store 中的状态构成了一个对象树.
Rule2:
State is read-only:
The only way to change the state is to emit an action, an object describing what happened.
状态在外界看来都是只读的, 改变状态的唯一途径就是向 store 派发 action.
可以将 action 抽象为一个对象, action 中就携带了所有的相关信息.
(在 swift 中也可以使用 enum 的关联值达到相同的目的, 且更加简洁地表达 action.)
Rule3:
在 Redux 的结构中, 还有 Reducer, 而 Reducer 就是动作的执行者, 它负责更新状态并返回新的状态给外界. Store 负责将 Reducer 和 State 关联起来, 保证特定的 Reducer 只能操作特定的 State.
而向 reducer 发送 action 的行为就叫 派发(dispatch)
.
Rule4:
To specify how the state tree is transformed by actions, you write pure reducers.
这里的 Pure reducer 指的是: 和纯函数类似, 总是有确定的输入和输出. 输入总是 action 和 state, 输出新的 state.
经过上面的总结, 可以发现 redux 的运行过程中的三个重要组成部分是:
- Store
- Reducer
- Action
再来回顾一下
Redux is a predictable state container for JavaScript apps.
意思是:
Technically, you want the
state
of your application to be managed by Redux.This is what makes Redux a state container.
这里解决一个认识上的问题: 当需要询问 “我还剩多少钱” 之类的问题是, 应该如何实现?
当然是在创建 store 的时候给它初值了! 不过许多初值都是从网络获取的, 故首先创建时候给一个空值是合理的.
网络数据的获取应该通过什么地方来传递给 state 更新呢? 看到 github flutter app 的做法是将网络数据关联给 action, 通过 action 中拿到数据并更新 state.
即: action 中将数据携带上, 进入到 reducer, 然后更新 state, 返回新的 state. 估计当返回新的 state 后就可以触发外部的响应操作了.(比如通知观察者更新视图.)
故思维上的转变就是这样的了:
Reducer 仅负责更新状态, 而更新状态所需要的数据都是通过 Action 携带来的!!!!
Store 仅是单纯的状态仓库!!!
Action 在语义上代表了需要 Store 执行状态更新的行为, 并且会携带状态更新所需的数据!!!
(在 JS 版本中, Action 经常被实现为普通的 JSON 对象, 更加通行的做法是将其视为两部分, 一部分是 type, 表示动作的类型, 一部分是 payload, 表示动作携带的数据! 这样的结构在 swift 中使用 enum 关联数据实现是再好不过了!)
Reducer 的实现实际非常简单, 但是由于有很长的 switch, 故看起来仍然不怎么好看, 实践中通常是:
针对每个状态都设置一个处理对象, 处理对象实现固定接口, 这样可以模拟单个小的 reducer 的效果, 从而无需使用switch.
总结:
需要一个方法来创建 Store.
Reducer 只是纯函数, 可以将它作为函数实现. 其中的具体处理方法可以放到其他地方.
将 Reducer 作为参数传递给 Store.
为什么会要求不要在 reducer 中 mutate state, 而是直接返回新的呢?
暂时先按这样的要求来做.
在 github flutter 工程中也是返回的新构造的状态. 那意味着在 swift 中状态用值类型比较好.
github 更新主页的操作就可以这样:
- 定义 Store, 持有当前主页上显示数据 State.
- 定义
ViewDidLoadAction
, 创建这个 Action 之前需要获取到当前主页所需的数据, 然后将数据作为 payload 放到 action 中. - 在 Reducer 中更新 Store 的状态.
- 观察者发现状态改变, 通过相关的方法来更新显示.
- 这样再次表明: State 的建模和服务端数据解析建模没有半毛钱关系 - _ -.
Swift 中的 Redux
正好接触到了 Flutter 中的 Redux 实现, 看了一下在 Swift 中也有这样的东西, 故看看实际使用效果, 如果好的话就先用这个, RxSwift 在商城场景下的确太牛刀了, 另外二者也可以结合使用, 不过感觉还是有点重量级了.
在 Github 上星星最多的是这个: https://github.com/ReSwift/ReSwift
这个库的 API 乍一看和 Flutter 中的十分类似, 这样也保证了在不同平台上的使用便利.
对于状态改变, 用到的是类似 onChange 的方式, 绑定方式稍有不同, 看是否也有绑定的形式的 API.
由于这里将状态信息都放到了 Core 层中, 实际上使用 Redux 是比较合适的, 表示层仅观察全局 store 中的所需状态分量就可以实现状态更新.
创建 Store 的时候首先把所需的 State 和 Reducer 准备好, 这样才能将二者进行绑定, Store 的主要作用就是将 Reducer 和对应的 State 进行绑定.
外界只需要将 action 派发给 store 做处理, 实际上就已经规定死了 store 可以进行的操作有哪些了.
在 VC 中对显示进行更新, 那实际上就可以直接在 VC 中根据动作进行 action 派发. 而无需经过中间层了, 不过更好的做法是由一组 VC 可以接触的接口来调用, 接口实现中处理 Action 的派发, 从而让表示层尽量不要接触到动作.
下层主要对动作进行处理.
这样的结构非常不错, 因为单一的数据源可以驱动任意位置的显示更新! 而且这样的结构有很大的优势, 就是结构简单清晰, 且代码量会减少很多! 相比起来在表示层无需使用 RxCocoa 改动这么大且需要适应的 API, 仍然可以用以前的一套逻辑来实现.
准备用 Redux 这一套实现之前的内容:
- 结合 Alamofire 实现网通通信层.
- 通过 Redux 来构造数据和界面的绑定.
在实现过程中要注意: Store 中的 State 可以直接被外界读取到, 所以不能留有除了动作以外的渠道去修改 State(主要就是把属性设置 getter), 这也是为什么要用值类型的原因.
使用示例已经放到这个库中了.