重新设计 Redux【译】
原文地址
状态管理不是一个已经解决的问题了吗?我觉得开发者们似乎知道了一个隐藏的真相:状态管理没必要像现在这么难。在这篇文章中,我们将要探索几个你很可能已经问过自己的问题:
- 你真的需要一个状态管理库吗?
- Redux是否值得得这样大受追捧?
- 我们能做出一个更好的状态管理方案吗?如果能,要怎样做?
状态管理真的需要一个库吗?
作为一个前端开发者并不只是移动像素,真正的开发艺术是知道把你的状态存到哪里。简单来说:这比较复杂,但又没那么复杂。
让我们看看在使用类似React这样的view框架时都有哪些选择:
1_kVjLQBND9Ji1QqgzgokHCA.png
1.组件状态(Component State)
存在于单个组件中的状态。比如在React中,用setState更新state。
2.相关的状态(Relative State)
父级传到子级的状态。比如在React中在用props作为属性传到子组件。
3. Provided State
保存在一个根** provider中的状态。并且不管在组件树上接近度如何,都可以访问。比如在React中提供的context这个API。
view中包含很多状态,反映了UI界面。但其他所有反映了基础数据和逻辑的代码怎么办?
把所有的东西都放到view中会违背软件开发中分类关注点**的原则:这使你纠缠于view框架,让你的代码难于测试,最烦人的可能是你还要不断地思考和整理到底把state保存在哪里。
状态管理由于设计改变这一事实而变得复杂,并且通常很难判断哪些组件需要哪种状态。最简单的选择是只提供根组件的所有状态,基于这点,再做选择可能会更好。
4. 外部状态(External State)
状态可以从你的view库中移出来。然后状态管理库使用生产者/消费者模式把他们“连接”起来保持同步。
Redux可能是最流行的状态管理库。过去两年它发展得极受欢迎。那为什么这样一个小库会备受青睐呢?
Redux性能更高吗?不,事实上,每个新的action都要处理会让它变得慢一些。
Redux更简单吗?当然不是。
简单应该是纯JavaScript:
屏幕快照 2018-03-28 下午4.45.34.png
那为什么不是每个人都用global.state = {}?
为什么是Redux?
本质上,Redux真的和TJ的根对象一样 ,只是包装在一个带着工具的管道中。
屏幕快照 2018-03-28 下午5.01.17.png
在Redux中,你不可以直接修改状态。唯一的方式是:分发一个Action到管道之中,让它最终来更新state。
在管道中有两种监听者:middleware和订阅(subscription)。Middleware是可以监听传入action的方法,可启用一些工具比如“logger”、“devtools”或者“syncWithServer”监听器。订阅是用来广播这些状态改变的方法。
最后,reducers是一些方法,它们可以将状态改变拆分成更小、更模块化、更可管理的分块。
开发过程中,比起用一个全局变量保存状态,用Redux确实是要简单一些。
把Redux看作是带着更新钩子函数的全局对象,而且是个转化下个状态的简单方式。
但Redux不会过于复杂吗?
是的。有几个不可否认的迹象表明一个API是有改进必要的。这些可以用下面的等式来总结:
屏幕快照 2018-03-28 下午6.04.47.png把time_saved认为是开发时你自己的解决方案可能会花费的时间,time_invested等同于阅读文档、学习教程、研究不熟悉的概念所投入的时间。
Redux本质上是一个学习曲线陡峭的小库。
这对于每个已经从Redux中克服困难并受益的开发人员来说,他们深入研究了函数式编程,与此同时可能已经失去可另一个潜在的开发人员,并且他认为“这不适合我,我要回到jQuery”。
用jQuery时你不需去明白“comonad”是什么,你也不用理解去处理状态管理的函数组合。
一个库的目的在于用抽象让复杂的问题看上去很简单。
要明确我的意图并不是怪罪Dan Abramov。
- 你怎么去重构一个已经有数百万开发者在使用的库?
- 您如何去辩解发布重大变更对世上不计其数的项目带来的影响?
你不能。但是你可以为广泛的文档、教育视频、技术社区提供很好的支持。Dan Abramov就赢在了这一点。
或许还有别的办法。
重新设计Redux
我认为Redux值得重写。带着七个值得改进的方面。
1.安装
让我们看看来自于官方示例官方示例的Redux基本安装。图片左侧
这仅仅是第一步,很多开发者就被迫暂停了,迷茫的看着眼前的深渊。thunk、compose这都是是什么鬼?一个函数还能这么搞事情?
考虑到如果Redux如果是基于配置而不是组合。安装更应该看起来像图右边。
2.简化的Reducer
Redux中的reducer中冗长但不必要的switch语句,虽然我们已经习惯这么用,但redux应该让我们远离它们。
屏幕快照 2018-03-28 下午7.30.33.png
假设reducer是用action的type来匹配的,我们可以反转参数,使得每个reducer都是一个纯函数接受状态和一个action。也许更简单一些,我们可以将action标准化并仅传递状态和payload。
3.Async/Await 替代 Thunks
** Thunks**通常被用来创建Redux中的异步action。在多种方法中,相比官方推荐的方案,thunk的工作方式更像是一种hack:
1.你需要dispatch一个action,这个Action实际上是一个function而不是所期望的对象。
- Thunk中间件检查每个action,看它们是不是个function
3.如果是的话,中间件执行这个function并且传入两个store的方法:dispatch和getState。
真的吗?action是一个简单的动态类型这个实践还不懒,比如说objec,function,甚至是Promise
屏幕快照 2018-03-28 下午7.50.47.png
就像这个例子里,我们能只用 async/await吗?
5.两种Action
当你思考它时,就已经有两种Action了:
- Reducer action:触发一个reducer并且改变状态。
- Effect action:触发一个异步Action。这可以调用一个Reducer action,但是异步方法不直接改变任何状态。
让这两种action区别开来可以更有帮助并且减少之前使用“thunks”所带来的困惑。
6.不要再多作为变量的Action类型
为什么把action creator和reducer区别对待是一种标准的实践?它们其中一个可以不要对方独立存在吗?改变其中一个不会影响另外一个吗?
action creator和reducer是一个硬币的正反面。
const ACTION_ONE = 'ACTION_ONE'是把action creator和reducer分离而产生的多余副作用。把两个看做一体,就没有必要导出大量的类型字符串文件了。
7.Reducers 就是Action Creators
根据用途对Redux的元素进行分组,您可能会想出一个更简单的模式。
屏幕快照 2018-03-28 下午11.39.03.png
可以通过reducer自动确定 action creator。总之,在这种情况下,reducer可以成为action creator。
使用基本的命名约定,以下内容是可预测的:
- 如果一个reducer有一个叫 “increment”的名字,那么类型就是 “increment”。更好的是,让我们来命名它:“count/increment”。
- 每个Action通过一个叫“payload”的键值来传递数据。
现在通过 count.increment,我们就可以从reducer中生成一个Action creator了。
好消息:我们能拥有一个更好的Redux
以上的痛点就是我们创造Rematch的原因。
Rematch是对Redux的一个包装,它在不损失任何可配置性的情况下提供了更简单的API。
屏幕快照 2018-03-29 上午9.39.58.png
以下是使用Rematch的一个完整例子:
屏幕快照 2018-03-29 上午9.41.47.png
过去几个月我一直在生产环境中使用Rematch。我可以证明:
我从未在思考状态管理时花费这么少的时间。
Redux并没有离开,也不会离开。它拥有Redux背后的简单模式,学习曲线少,样板少,认知开销少。
试试Rematch吧,看看你到底喜不喜欢。
在帮我们点个star,好让我们知道你已经试过了。
网友评论