首发于前端小记

redux 实践中的的策略考量(下)

在上篇中讨论了以下几个问题,欢迎查阅

  • 重复造轮子还是抽象
  • 结构嵌套还是关系化存储
  • 简单指令集还是复杂指令集

地址:redux 实践中的的策略考量(上) - 知乎专栏


单一状态树?

redux 官方总是推荐单一状态树的。但是,实际情况确实不见得都必须单一,有的时候冗余比单一的好用。


有时组件单独抽离一个 redux store 有着许多好处,比如少了不必要的 reducer 配置,可以用在其他不使用 redux 的框架中等等。但是,这是组件相对独立的前提下才有的优势,否则如果有其他的 container 想要读取某个组件状态那就 gg 。


而多 store 的设计,大概最能参考的是 reflux,个人觉得最大的问题在于 store 与 store 之间相互订阅容易导致很多问题,也让 bug 的追踪变得麻烦。


而为什么要用单一状态树?最早 dan 的需求是实现时间机器,这就需要有一个中心可以统一的对所有状态的变更进行管控与记录,而使用纯函数的方式也是出于这个考虑:更利于对行为的输入输出进行分离与记录。估计是在这之后才发现,噢,这对调试也有极大的便利,包括框架统一为函数式风格之后对于代码的可读性也有着质的飞跃。


所以,个人感觉单一状态树并不是必须,但如果想分离 store 的话,得把具体的情况摸透考虑清楚。这之中需要考量挺多细节:


  1. 复杂度如何,是否会频繁维护,出错可能性多高;
  2. 与其他状态的耦合情况怎样,如果耦合,在 thunk action creator 里能读取其他状态这很重要;
  3. 项目紧迫性要求如何,如果紧迫性高,未经良好设计的 reducer / actions 有可能还不如直接用 inline state;
  4. 是否在最开始就要考虑测试,不管是集成测试还是单元测试。

大多数情况下,一个 App 的确只有一个 store 就已经足够。


Middleware 还是 Higher Order Reducer

很多时候 Middleware 都能和 Higher Order Reducer (简称 HoR) 产生类似作用,都可以给原本的数据处理添加功能。


Middleware 更加强大,是在所有 action 传到 reducer 之前能对 action 进行很多处理然后自行再做 dispatch 的行为,所以经常用于支持更多类型的 action 的支持,比如 redux-thunk / redux-promise 分别支持了函数与Promise 的 action 形态;或者对 action 的增强 / 统一处理,比如 DevTools / logger 等。middleware 显然会对所有传入的 action 进行处理,而且显然其在 store 配置时的顺序有可能造成不同的影响与结果,所以使用时应该尽可能小心。


HoR 相对来说更加倾向于保守与纯粹,因为总的来说他就只做了对 reducer 的构建 / 包装工作,而且其中能用上的参数其实与 reducer 没有区别;更多的,HoR 不是一个特性,他只是函数式风格里的一种思路罢了。但是,因为其函数式的特性风格以及原来 reducer 的特性, HoR 函数方法不应该有副作用(输入输出之外没有对其他内容进行依赖或者修改),而应该是个纯函数,所以虽然 logger 可以放在 HoR 层,但更多大家还是把他放在 middleware 层,我想这就是其中的原因。


总的来说,我认为 middleware 虽然强大但不应该乱用,能用 HoR 解决的尽量都用 HoR,而 HoR 的实现很有可能会对代码设计能力有很高的要求。


什么时候不使用 redux

redux 相对于其他状态框架的最显著区别就是函数式。所以,从定位上来说,有很多因素都可以让你不选择 redux:


  • 不需要全局状态管理:状态之间没有这么多耦合的时候的确不需要
  • 不喜欢单一状态树(当然,可以通过多 store 解决)
  • 不感冒函数式风格,毕竟,对象式、响应式也各有其优势,而且也有足够多的相关状态框架
  • 不能忍受其学习或是基础开发成本

函数式本身是很基础的东西,也不见得一定要 redux 才能实现,包括 setState 也都可以传入一个函数来进行修改: (state, props) => newState ;甚至,自己在组件内封装一个 dispatch 和 reducer 其实也可以完全符合 redux 的风格,不见得都要接入到 redux 。当然,自己实现 dispatch / reducer 小规模还可以,大规模的话还是算了,毕竟 redux + react-redux 做了很多常人难以想象的优化,就别自找麻烦重新做一遍了。


actionCreator 是纯执行还是支持传入回调?

最典型的就是调用 actionCreator 返回一个 Promise,虽然这不太符合 FLUX 架构。比如我可以这样:


componentWillReceiveProps(newProps) {
	if (newProps.someReduxState === 'changed'
		&& newProps.someReduxState !== this.props.someReduxState)
	{
		// DO SOMETHING
	}
}

可以看到,redux 对于事件监听的支持实在是不太友好,毕竟他是纯函数式的风格,当然天然的对事件风格的支持会比较糟糕。


最主要的场景大概来自于导航里的路由 stacks 结构并没有放到 store 里,比如需要按钮点击以后先做一些异步操作之后跳转到某个界面,这种情况下如果不使用 Promise 的话,处理起来会麻烦很多而且代码可读性很差。


也许这里最好的解决方案就是,把原本事件式的逻辑,使用函数式的风格改造。就像现在很多 router 内部的设计就是引入 FLUX 架构,把整个路由状况存成 stack,根据 stack 来渲染界面。甚至,会支持把 dispatch 与 state 外置到类似 redux 的状态管理中,那么只要在相关框架的思路来进行路由就可以了,也就不用到视图这一层来处理。


其次的解决方案是,action 虽然要求是纯函数,但 actionCreator 是可以传入任意参数包括回调的,比如,把 onSuccess 传递给 actionCreator 让其在成功时调用,这也是其中一个解决方案。但是,这个方案做不到跨组件事件监听,比如组件 A 触发 actionCreator 成功后组件 B 对成功事件进行单次响应,不好意思,做不到就是做不到……


总的来说,redux 做的是状态管理而非事件管理,如果需要事件管理,也许另外开发一个工具或者开发一个 middleware 比较合适。

编辑于 2017-05-05 11:59