Redux 学习起来很困难?写起代码来很啰嗦?
一块儿来看看 Rematch 的做者对 Redux 的思考和简化吧~前端
原文:《Redesigning Redux》, Shawn McKaygit
都过了这么多年了,状态管理的问题难道不该该早就被解决了么?
我的直觉,开发者彷佛都认可一个潜规则,那就是状态管理问题彷佛比想象的更复杂。在本文,咱们将一块儿探讨你可能已经遇到的问题:github
前端开发并不只仅是左右移动像素点。开发的真正艺术是掌握如何管理状态。
简单粗暴的答案是:状态管理是复杂的,可是并不是那么复杂。编程
以基于组件的视图框架/库为例,好比 React,让咱们来看看咱们有哪些选择:redux
存在于单个组件内部的状态。在 React 中,就是指使用 setState
来更新的 state
。数组
父组件传递给子组件的状态。在 React 中,就是指经过属性传递给子组件的 props
。架构
保存在根提供者中、经过组件树传递给消费者的状态。在 React 中,对应于 context API
。框架
大多数的状态都是存在于视图中的,由于它是用来反映用户界面的。那么,对于反映底层数据和逻辑的其它状态,又属于谁呢?异步
若是把全部的状态都填塞到视图中,那么就严重违背了关注点分离原则。它会把你紧紧地绑在一个 JS 视图库中,使得代码难以测试。更有甚者,可能会为你带来大麻烦,由于你必须持续不断的思考和调整状态的存储之处。async
因为架构设计的改变,状态管理会随之变得复杂,而且一般很难判断哪些组件须要哪些状态。最简单的作法是,在根组件上包含全部状态。若是真要这么作的话,那么选用下一种方式会更好。
状态是能够从视图库中移出来的,而后可使用提供者/消费者模式把状态从新链接回视图库。
Redux 也许是最流行的状态管理库。它在过去的 2 年时间里取得了巨大的流行度。那么,为何一个简单的库会得到如此之多的关注呢?
是由于它的性能更高么?其实并非。实际上,它反而让每个必须处理的回调变得更慢了些。
那是由于它更简单?也决定不是。
论简单的话,那么纯 JS 才是。正如 TJ 所说:
那为何你们不直接使用 global.state = {}
呢?
本质上,Redux 跟 TJ 所说的是同一件事,只不过 Redux 封装了一些管道工具而已。
在 Redux 中,咱们不能直接修改状态。修改状态的惟一方式是分发(Dispatch)一个动做(Action)到管道中,管道会自动根据动做去更新状态。
从上图能够看到,管道的中有 2 个监听器集合:中间件(Middleware)和订阅(Subscription)。中间件一些是监听动做的函数,用来处理相似日志记录、开发工具和发起服务请求等功能。订阅则是一些用来广播状态变动的函数。
最后,合成器(Reducer)函数负责把状态变动拆分红更小、更模块化、更容易管理的代码块。
和使用一个全局对象相比,Redux 确实简化了开发过程。
综上,咱们能够把 Redux 当作是一个全局对象,该对象不只提供了状态更新前/后钩子,并且可以以简单的方式合成新状态。
的确过于复杂。在平时的开发过程当中,有一些不能否认的迹象,能够用来判断框架 API 是否须要改进。这些迹象能够概括为下面这个公式:
其中,节省的时间是指使用该框架来开发所消耗的时间,学习的时间则是阅读框架文档、学习教程和掌握新概念的总时间。
Redux 自己是个精简的库,可是其学习曲线却很陡峭。虽然有很多开发者可以克服深刻学习函数式编程的困难并从 Redux 获益良多,可是也有不少开发者望而却步,宁愿从新使用 jQuery。
在 jQuery 中,你并不须要理解什么是 “comonad”,也不必理解经过函数组合来管理状态。
任何框架或者库的目的都应该是把复杂的事物抽象得更加简单。
固然我这么说并非想指责 Redux 的做者 Dan Abramov 。Redux 在其刚诞生初期就已经变得很是流行,没有足够的时间来精雕细琢。
没人能够。可是咱们能够提供更好的支持,好比完善文档、推广视频教程和扩大社区等。在这些方面,Dan Abramov 确实作得很棒。
又或者,还有另外一种方式能够改变现状。
在我看来,重写 Redux 是有其必要性的,至少有如下 6 个方面能够改进得更友好。
让咱们来看看一个基本的 Redux 初始化过程,以下图左边所示:
不少开发者走到这里一步就开始止步了,简直是一看三不知。什么是 chunk ?compose 又是什么鬼?一个函数还能这么使用?
若是 Redux 是基于配置而不是函数组合的话,那么像右边那样的初始化过程明显看起来更加合理。
Redux 中的状态合成器可以使用一个 switch
来代替多个没必要要的 switch
。
假如状态合成器是根据动做的类型来匹配的,那么咱们能够用逆向思惟,把合成器变成一个接受 state
和 action
两个参数的纯函数。也许还能够更简单些,咱们能够把动做规划化,而且只传递状态和一个数据载荷。
在 Redux 中,Thunks最通用的作法就是用来建立异步动做。从多角度来看,这种用法更像是一个聪明的黑客所采用的用法,而不是一种官方推荐的用法。咱们一步一步来看:
dispatch
和 getState
方法传参进去真的须要这样么?把一个简单的动做在运行时识别为对象、函数甚至是 Promise,这难道不是糟糕的实践么?
如上图右边所示,难道咱们就不能只使用 async/await ?
若是咱们认真想一想的话,确实有两种类型的动做:
所以,把这两种类型的动做区分开来会更有用,同时也不容易与 Thunks 搞混。
为何咱们的标准实践要把动做生成器和状态合成器区分开来呢?可否只用其中一个呢?改变其中一个又是否会影响到另外一个?
在我看来,动做生成器和状态合成器就是硬币的两个面。
const ACTION_ONE = 'ACTION_ONE'
能够说是把动做生成器和状态合成器分开的冗余产物。若是咱们把这二者合二为一,那么就不会有那么多文件专门导出这些类型字符串了。
按照使用方式,把 Redux 中所涉及的概念进行合并分组,那么咱们能够得出下面这个更简单的模式。
在这种模式中,状态合成器是能够自动肯定与之对应的动做生成器。由于,此时状态合成器能够自动变成动做生成器。
经过简单的命名约定,如下行为都会变得可预测:
increment
,那么动做类型就是 increment
。更好的作法是加上命名空间: count/increment
。payload
属性来传递数据。这样的话,对于 count.increment
,咱们就能够自动的从状态合成器推导出动做生成器。
以上的通电就是咱们建立 Rematch 的缘由。
Rematch 对 Redux 进行了封装,提供更简单的 API,但又不失任何可配置性的特色。
下面是一个完整的使用例子:
我已经把 Rematch 应用在生成环境中有好几个月了。做为小白鼠,个人体验是:
状态管理从未变得如此简单、高效。
Redux 并无被抛弃,并且也不该该被抛弃。
只是,咱们应该以更低的学习成本,更少的样板代码和更少的认知成本,来拥抱 Redux 背后的简单哲学。
赶忙试一试 Rematch 吧!万一一不当心你就爱上它了呢?