React Hooks的丑陋一面


React Hooks的丑陋一面

文章插图
 
在这篇文章中,我将分享我对React Hooks的观点,正如这篇文章的标题所暗示的那样,我不是一个忠实的粉丝 。
让我们来分析一下React官方的文档中描述的放弃类而使用钩子的动机 。
动机1:class令人困惑
我们发现,class可能是学习React的一大障碍,你必须了解 this 在JAVAScript中的工作方式,这与大多数语言中的工作方式截然不同 。你必须记住要绑定事件处理程序,代码会非常啰嗦,React中函数和类组件之间的区别,以及何时使用每个组件,甚至在有经验的React开发人员之间也会导致分歧 。
好吧,我可以同意 this 在你刚开始使用JavaScript的时候可能会有点混乱,但是箭头函数解决了混乱,把一个已经被Typescript开箱即用支持的第三阶段功能称为“不稳定的语法建议”,这纯粹是煽动性的 。React团队指的是class字段语法,该语法已经被广泛使用并且可能很快会得到正式支持:
class Foo extends React.Component {onPress = () => {console.log(this.props.someProp);}render() {return <Button onPress={this.onPress} />}}如你所见,通过使用class字段箭头函数,你无需在构造函数中绑定任何内容,并且它始终指向正确的上下文 。
如果Class令人困惑,那么对于新的钩子函数我们能说些什么呢?钩子函数不是常规函数,因为它具有状态,看起来很奇怪的 this(又名 useRef ),并且可以具有多个实例 。但这绝对不是类,介于两者之间,从现在开始,我将其称为 Funclass 。那么,对于人类和机器而言,那些Funclass会更容易吗?我不确定机器,但我真的不认为Funclass从概念上比类更容易理解 。
类是一个众所周知的思想概念,每个开发人员都熟悉 this 的概念,即使在javascript中也有所不同 。另一方面,Funclass是一个新概念,一个很奇怪的概念 。它们让人感觉更神奇,而且它们过于依赖惯例而不是严格的语法 。你必须遵循一些严格而奇怪的规则,你需要小心你的代码放在哪里,而且有很多陷阱 。还要准备好一些可怕的命名,比如 useRef( this 的花哨名字)、useEffect、useMemo、useImperativeHandle(说什么呢?)等等 。
类的语法是为了处理多实例的概念和实例范围的概念(this 的确切目的)而专门发明的 。Funclass只是一种实现相同目标的奇怪方式,许多人将Funclass与函数式编程相混淆,但Funclass实际上只是变相的类 。类是一个概念,而不是语法 。
在React中,函数和类组件之间的区别,以及何时使用每一种组件,甚至在有经验的React开发人员之间也会产生分歧 。
到目前为止,这种区别非常明显——如果需要状态或生命周期方法,则使用类,否则,使用函数或类实际上并不重要 。就我个人而言,我很喜欢这样的想法:当我偶然发现一个函数组件时,我可以立即知道这是一个没有状态的“哑巴组件” 。遗憾的是,随着Funclasses的引入,情况不再是这样了 。
动机2:很难在组件之间重用有状态逻辑具有讽刺意味吗?至少在我看来,React最大的问题是它没有提供一个开箱即用的状态管理方案,让我们对应该如何填补这个空白的问题争论了很久,也为Redux等一些非常糟糕的设计模式打开了一扇门 。所以在经历了多年的挫折之后,React团队终于得出了一个结论:组件之间很难共享有状态逻辑......谁能想到呢?
无论如何,勾子会使情况变得更好吗?答案是不尽然 。钩子不能和类一起工作,所以如果你的代码库已经用类来编写,你还是需要另一种方式来共享有状态的逻辑 。另外,钩子只解决了每个实例逻辑共享的问题,但如果你想在多个实例之间共享状态,你仍然需要使用stores和第三方状态管理解决方案,正如我所说,如果你已经使用它们,你并不真正需要钩子 。
所以,与其只是治标不治本,或许React是时候行动起来,实现一个合适的状态管理工具,同时管理全局状态(stores)和本地状态(每个实例),从而彻底扼杀这个漏洞 。
动机3:复杂的组件变得难以理解如果你已经在使用stores,这种说法几乎没有意义,让我们看看为什么 。
class Foo extends React.Component {componentDidMount() {doA();doB();doC();}}在这个例子中,你可以看到,我们可能在 componentDidMount 中混合了不相关的逻辑,但这是否会使我们的组件膨胀?不完全是 。整个实现位于类之外,而状态位于store中,没有store 所有状态逻辑都必须在类内部实现,而该类确实会臃肿 。但看起来React又解决了一个问题,这个问题大多存在于一个没有状态管理工具的世界里 。实际上,大多数大型应用程序已经在使用状态管理工具,并且该问题已得到缓解 。另外,在大多数情况下,我们也许可以将这个类分解成更小的组件,并将每个 doSomething() 放在子组件的 componentDidMount 中 。


推荐阅读