本文是借助网易见外的工具帮助完成翻译的,网易见外是基于NMT(神经网络机器翻译)技术的海外内容智能聚合平台 原文地址: https://hackernoon.com/redesigning-redux-b2baee8b8a38
到目前为止,状态管理不应该是一个已经解决了的问题吗?直觉上,开发者似乎知道一个隐藏的真相:状态管理似乎比它需要的更要难。这篇文章里,我们尝试回答一些你可能一直在问自己的问题:
- 你真的需要一个状态管理库吗?
- Redux的流行是否值得?为什么或者为什么不呢?
- 我们能不能做一个更好的状态管理方案?如果能,怎么去做?
状态管理真的需要一个库吗?
作为一个前端开发人员,不仅仅是控制像素;真正的开发之美是知道在哪里存储你的状态。简而言之:这很复杂,但又不复杂。
让我们看看使用React等基于组件的视图框架/库时的选项:
1. Component State
存在于单个组件内部的状态。在React中,通过setState
方法更新state
。
2. Relative State
状态从父组件传递给子组件。在React中,通过子组件上的props
属性传递。
3. Provider State
状态保存在根 provider (提供者) 组件中,并由 consumer (消费者) 在组件树的某个地方访问,而不考虑距离。在React中,通过context API
可以实现。
很多状态都属于视图,因为它反映了UI。但是,其他反映你底层数据和逻辑的代码呢?
把所有内容都放到视图中会丧失关注点分离:它将你拴在到了某个javascript视图库上,这使得代码更难测试,可能最大的麻烦是:你必须不断思考和重新调整存储状态的位置。
由于设计经常改变,而且通常很难判断哪些组件需要哪个状态,所以状态管理变得复杂。最简单的选择就是从根组件中提供所有的状态,在这一点上,你最好选择下一个选项。
4. External State
状态可以移到视图库之外。然后,库可以使用提供者/消费者模式“连接”,以保持同步。
也许最受欢迎的状态管理库是Redux。在过去的两年里,它变得越来越受欢迎。那么,为什么大家对一个简单的库如此热爱呢?
Redux更具性能?答案是否定的。事实上,为了每一个必须处理的新动作(action),都会稍微慢一些。
Redux是否更简单?当然不是。
简单应当是纯javascript:比如 TJ Holowaychuk 在twitter上说,他的状态管理库是 {}
。
那么为什么不是每个人都使用 global.state={}
?
为什么是Redux?
在表层之下,Redux与TJ的根对象{}
完全相同——只是包装在了一系列实用工具的流水线(pipeline)中。
在Redux中,你不能直接修改状态。只有一种方法:将一个 动作(action)分派(dispatch) 到流水线中,最终更新状态。
在流水线中有两组侦听器:中间件(middleware) 和 订阅(subscription) 。中间件是能够侦听传入的操作的函数,从而支持诸如“logger”、“devtools”或“syncWithServer”侦听器之类的工具。订阅是用来广播这些状态变化的函数。
最后,还原器(reducer) 的更新方法可以将状态变化分解为更小、更模块化和可管理的块。
Redux实际上可能比使用一个全局对象作为你的状态更简单。
可以将Redux看作一个具有前置/后置更新挂钩的全局对象,并简化了“还原(reducing)”下一个状态的方法。
但是不是Redux太复杂了?
是的。有几个明确的迹象表明API需要改进。这些可以用下面的公式来总结:
API的质量 = 代码行数 / 花在阅读文档上的时间
Redux是一个拥有陡峭学习曲线的小型库。对于每一个已经克服并受益于Redux的开发人员来说,他们都是在深入研究函数式编程,另一些潜在的开发者已经失败了,并且认为“这不是我要的,我要回到jQuery”。
使用jQuery你不需要理解“monad”是什么,你也不需要为了使用Redux去理解函数组合。
任何库的目的都是通过抽象来让更复杂的东西看起来简单。
我的意思并不是要怼 Dan Abramov(Redux的主要提交者之一)。Redux在它早期太稚嫩的时候就已经太受欢迎了。
- 如何重构一个已经被数百万开发者使用的库?
- 你如何决定为世界各地无数的项目引入不兼容的变更?
你不能。但是你可以通过大量的文档、教学视频和社区活动提供惊人的支持。Dan Abramov就是在这里获胜的。
或许还有另一种方法。
重新设计Redux
我认为Redux值得重写,并且带来了7处需要改进的地方。(译注:原文缺失第3点,实际只有6处)
1. 设置
让我们看一看左边的真实世界Redux示例的基本设置。
许多开发人员在第一步后就在这里暂停,茫然地盯着深渊。 什么是 thunk?compose?一个函数能做到这些吗?
倘若Redux可以基于配置而非组合。设置可能看起来更像右边的示例。
2. 简化 reducers
Redux中的reducers可以通过一个转换,让我们远离已经习惯但不必要且冗长的switch语句。
既然一个reducer是为了匹配动作的类型(action.type),那么我们可以对参数进行反转,以便让每个reducer都是一个接受状态和动作的纯函数。也许更简单,我们可以标准化动作,只传递状态和有效负载(payload)。
4. Async/Await 与 thunks
在Redux中,通常使用thunks来创建异步操作。在很多方面,一个 thunk 的工作方式看起来更像是一个聪明的黑客,而不是官方推荐的解决方案。跟我来往下看:
- 你分派一个动作(dispatch an action),它实际上是一个函数而不是预期的对象。
- 这个中间件检查每一个动作,看它是否是一个函数。
- 如果是,中间件调用该函数,并传入一些 store 的方法:dispatch 和 getState。
怎么会这样?一个简单的action 到底是作为一个动态类型的对象、一个函数,还是一个 Promise?这难道不是一种拙劣的实践吗?
就像右边的例子一样,我们难道不能只是简单地使用 async/await 就可以了吗?
5. 两种 actions
当你思考这个问题的时候,实际上有两种actions:
- reducer action:触发一个reducer并改变状态。
- effect action:触发一个异步操作。这可能会调用reducer action,但是异步函数不会直接改变任何状态。
区分这两种类型的 actions 会更有帮助,并且不会让上述用法与“thunks”混淆。
6. 不再有动作类型(action.type)变量
为什么用不同的方式来对待 action creators 和 reducers 是一种标准做法?两者之一可以独立于另一方而存在吗?改变其中之一不会影响另一方吗?
action creators 和 reducers 是同一枚硬币的两面
const ACTION_ONE = 'ACTION_ONE'
是分离 action creators 和 reducers 的一个多余的副产品。应将两者视为一体,并且不再需要文件导出类型的字符串。
7. reducers 就是 action creators
通过使用Redux的元素来分组,你可能会得到一个更简单的模式。
它可以自动地从reducer中确定action creator。毕竟,在这个场景中,reducer可以成为action creator。
使用一个基本的命名约定,下面是可预测的:
- 如果一个reducer有“increment”的名字,那么类型就是“increment”。更妙的是,我们还给它增加了一个命名空间“count/increment”。
- 每个action都通过一个“payload”键传递数据。
现在,从 count.increment
中,我们可以以一个reducer生成action creator。
好消息:我们可以有一个更好的 Redux
这些痛点是我们开发Rematch的原因。
Rematch围绕Redux进行了封装,提供更简单的API,而不丢失任何可配置性。
请参见下面的一个完整的Rematch示例:
在过去的几个月里,我一直在实际业务中使用Rematch。作为证明,我会说:
我从未花费如此少的时间去考虑状态管理。
Redux不会消失,也不应该消失。拥抱Redux之后的简单模式,学习曲线更平滑,样板代码(boilerplate)更少,认知开销也更少。
试试Rematch,看看你是否喜欢它。并且在github上给我们一个 star 以便让别人也知道。
原文发布时间:2018年03月
本文来源:前端外刊 如需转载请联系原作者