在这篇文章中,我将分享我对ReactHooks的看法,正如这篇文章的标题所暗示的,我不是一个超级粉丝。我们来分析一下React官方文档中描述的放弃类和使用hooks的动机。动机1:类令人困惑我们发现类可能是学习React的一大障碍,你必须了解它在JavaScript中的工作方式,这与它在大多数语言中的工作方式有很大不同。你必须记住绑定事件处理程序,代码可能会变得非常冗长,React中函数和类组件之间的区别,以及何时使用它们,甚至在经验丰富的React开发人员中也会引起分歧。好吧,我同意当你刚开始使用Javascript时这可能有点混乱,但是箭头函数解决了这种混乱,调用了Typescript已经支持的第三阶段功能“不稳定的语法建议”,纯属煽动.React团队参考了类字段语法,被广泛使用,可能很快就会被官方支持:classFooextendsReact.Component{onPress=()=>{console.log(this.props.someProp);}render(){return}}如你所见,通过使用类字段箭头函数,你不需要在构造函数中绑定任何东西,它总是指向正确的上下文.如果Class令人困惑,那么关于新的钩子函数我们能说些什么呢?钩子函数不是常规函数,因为它有状态,一个看起来很奇怪的this(又名useRef),并且可以有多个实例。但它绝对不是一个类,介于两者之间的东西,从现在开始我将其称为Funclass。那么,这些Funclass对人类来说会更容易吗?ns和机器?我不确定机器,但我真的不认为Funclasses在概念上比类更容易理解。类是一个众所周知的思想概念,每个开发人员都熟悉这个概念,即使在javascript中也是如此。另一方面,Funclass是一个新概念,一个非常奇怪的概念。他们感觉更神奇,他们过于依赖惯例而不是严格的语法。你必须遵循一些严格而奇怪的规则,你需要小心你的代码放在哪里,而且有很多陷阱。还要为一些可怕的命名做好准备,例如useRef(this的奇特名称)、useEffect、useMemo、useImperativeHandle(说什么?)等。类的语法是专门为处理多个实例的概念和实例范围的概念而发明的(这个的确切目的)。Funclasses只是实现相同目标的一种奇怪方式,许多人将Funclasses与函数式编程混淆,但Funclasses实际上只是伪装的类。类是一个概念,而不是语法。在React中,函数和类组件之间的区别,以及何时使用它们,即使是经验丰富的React开发人员也会产生分歧。到目前为止,区别非常明显——如果您需要状态或生命周期方法,请使用类,否则,使用函数还是类并不重要。就个人而言,我喜欢这样的想法,即当我偶然发现一个功能组件时,我可以立即知道它是一个没有状态的“哑组件”。遗憾的是,随着Funclasses的引入,情况不再如此。动机2:很难在组件之间重用有状态逻辑,这是否具有讽刺意味?React最大的问题,至少在我看来,是它没有提供开箱即用的状态管理解决方案。让我们弄清楚如何填补这个空白已经争论了很长时间,并且也为一些非常糟糕的设计模式打开了大门,比如Redux。所以经过多年的挫折,React团队终于得出了一个结论:很难在组件之间共享有状态的逻辑……谁会想到呢?无论如何,钩子会让事情变得更好吗?答案并不尽然。钩子不适用于类,所以如果你的代码库已经写在类中,你仍然需要另一种方式来共享有状态逻辑。还有,hooks只是解决了per-instance的逻辑共享问题,但是如果你想在多个instance之间共享state,你还是需要使用store和第三方状态管理方案,正如我所说,如果你已经使用它们,你就不会真的需要钩子。因此,与其治标不治本,也许是时候让React继续前进并实施一个适当的状态管理工具来管理全局状态(存储)和本地状态(每个实例)以完全消除此漏洞。动机3:复杂的组件变得难以理解如果您已经在使用商店,那么这个说法就没有什么意义,让我们看看为什么。classFooextendsReact.Component{componentDidMount(){doA();doB();doC();}}在这个例子中,你可以看到我们可能在componentDidMount中混入了不相关的逻辑,但这会让我们的Component膨胀吗?不完全的。整个实现在类之外,状态在store中,如果没有store,所有状态逻辑都必须在类内部实现,类确实会臃肿。但看起来React解决了另一个主要存在于没有状态管理工具的世界中的问题。事实上,大多数大型应用程序已经使用状态管理工具,并且这个问题已经得到缓解。此外,在大多数情况下,我们可以将这个类分解成更小的组件,并将每个doSomething()放在子组件的componentDidMount中。使用Funclass,我们可以编写如下代码:functionFoo(){useA();使用B();使用C();}看起来很干净,但是真的吗?我们还需要在某处编写3个不同的useEffect挂钩,所以最后我们将编写更多代码,看看我们在这里做了什么-使用类组件,您可以一眼看出组件在挂载时做了什么。在Funclass示例中,您需要跟随钩子并尝试使用空依赖项数组搜索useEffect以找出组件在挂载时执行的操作。生命周期方法的声明性本质上是一件好事,我发现研究Funclasses的流程要困难得多。我见过很多Funclasses使开发人员更容易编写错误代码的案例,我们稍后会看到一个例子。但首先,我不得不承认useEffect有一些好处,请看下面的例子:useEffect(()=>{subscribeToA();return()=>{unsubscribeFromA();};},[]);useEffect挂钩允许我们将订阅和取消订阅逻辑配对在一起。这实际上是一个非常简洁的模式,就像将componentDidMount和componentDidUpdate配对在一起一样。根据我的经验,这些情况并不常见,但它们仍然是useEffect真正有用的有效用例。问题来了,为什么一定要用Funclass来获取useEffect呢?为什么我们班不能有那样的东西呢?答案是我们可以:(this.props.value1,this.state.value2);returnHelloworld}}effect函数会记住持有给定的函数,并且只有当它的一个参数发生变化时才会再次调用它。通过在我们的渲染函数内部触发效果,我们可以确保在每次渲染/更新时调用它,但只有当它的一个参数发生变化时,给定的函数才会再次运行,所以我们结合componentDidMount和componentDidUpdate来实现一个类似useEffect的效果,可惜我们还是需要在componentWillUnmount中手动进行最后的清理。此外,从render中调用效果函数有点难看。为了得到和useEffect完全一样的效果,React需要增加对它的支持。底线是useEffect不应被视为进入有趣课程的有效动机,它本身就是一个有效动机,也可以在课程中实施。动机4:性能React团队说类很难优化和最小化,应该以某种方式改进funclasses,对此我只有一件事要说-给我看数字。到目前为止,我找不到任何论文,也找不到可以克隆和运行以比较FunclassesVSClass性能的基准演示应用程序。事实上,我们没有看到这样的演示并不奇怪——Funclasses需要以某种方式实现这一点(如果你愿意,请参考),所以我很期待让类难以优化的问题,并将影响Funclasses。不管怎样,所有关于性能的争论在没有展示数据的情况下真的毫无价值,所以我们真的不能拿它来作为争论。动机5:Funclass不那么冗长你可以找到许多通过将Class转换为Funclass来减少代码的例子,但大多数(如果不是全部的话)都使用useEffect钩子来结合componentDidMount和componentWillUnmount来实现很好的效果。但正如我之前所说,useEffect不应被视为Funclass的优势,如果忽略它实现的代码减少,那么只剩下很小的影响。此外,如果您尝试使用useMemo、useCallback等优化Funclass,您甚至可能会得到比等效类更冗长的代码。当比较小而琐碎的组件时,Funclasses毫无疑问地获胜,因为类有一些固有的模板,无论你的类有多小,你都需要付出代价。但是当比较更大的组件时,你几乎看不出区别,有时就像我说的,类甚至可以更干净。最后,关于useContext不得不说几句:useContext其实是对我们现在原有类的contextAPI的一个很大的改进。但同样,为什么我们不能为类也提供如此漂亮和干净的API?为什么我们不能做这样的事情。//在“./someContext”中:exportconstsomeContext=React.Context({helloText:'bla'});//在“Foo”中:import{someContext}from'./someContext';classFooextendsReact.component{render(){{someContext.helloText}}}当上下文中的helloText发生变化时,组件应该重新渲染以反映这些变化。就是这样,没有丑陋的高阶组件(HOC)。那么为什么React团队选择只改进useContextAPI而不是常规的内容API?我不知道,但这并不意味着Funclass天生就更干净。这意味着React应该通过对类实施相同的API改进来做得更好。因此,除了关于动机的问题,让我们看看我不喜欢Funclass的其他地方。隐藏的副作用关于Funclasses的useEffect实现,最让我困扰的事情之一是没有弄清楚组件的副作用是什么。对于类,如果您想知道组件在挂载时做了什么,您可以轻松地检查componentDidMount中的代码或检查构造函数。如果您看到重复调用,您可能应该检查componentDidUpdate,使用新的useEffect钩子,副作用可以嵌套在代码中。假设我们检测到一些不必要的服务器调用,我们查看可疑组件的代码并看到以下内容:constrenderContacts=(props)=>{const[contacts,loadMoreContacts]=useContacts(props.contactsIds);return()}这里没什么特别的,是研究SmartContactList,还是钻研useContacts?让我们深入研究useContacts:{//**许多其他代码与加载联系人的某些动画相关。*//},[loadingStatus]);//..restofcode}好的,这就是它变得棘手的地方。隐藏的副作用在哪里?如果我们深入研究useSwipeToRefresh,我们会看到:();//bingo!我们隐藏的副作用}});//<==我们忘记了依赖数组!我们发现了我们的隐藏效果,refreshContacts会在每个组件渲染时意外地调用fetchcontacts。在大型代码库和一些结构不良的组件中,嵌套的useEffect会造成麻烦。我并不是说你不能用类写出糟糕的代码,但是Funclasses更容易出错,而且没有严格定义生命周期方法的结构,更容易做坏事。臃肿的API通过在类旁边添加hooksAPI,React的API有效地翻了一番。现在每个人都需要学习两种完全不同的方法,不得不说,新的API比旧的要晦涩得多。像获取以前的道具和状态这样简单的事情现在是很好的面试材料。你能写一个钩子来获取以前的道具而不求助于谷歌吗?像React这样的大型库必须非常小心地在API中添加如此巨大的变化,以至于这样做的动机甚至都不合理。缺乏描述性在我看来,Funclass比class更令人困惑。例如,更难找到组件的入口点——使用类只需搜索渲染函数,但使用Funclasses则很难找到主要的返回语句。此外,与常规生命周期方法相比,很难根据不同的useEffect语句来理解组件流,这些常规生命周期方法会为您提供一些关于代码需要查看的良好提示。如果我正在寻找某种初始化逻辑,我会跳转(VSCode中的cmd+shift+o)到componentDidMount,如果我正在寻找某种更新机制,我可能会跳转到componentDidUpdate,等等。使用Funclass,我发现很难在大型组件中定位。约定驱动的API挂钩的主要规则之一(可能也是最重要的)是使用前缀约定。只是感觉不对你知道什么感觉不对吗?这就是我对钩子的感觉。有时我可以准确指出问题所在,但有时这只是一种普遍的感觉,即我们走错了方向。当你找到一个好的概念时,你可以看到事情是如何很好地结合在一起的,但是当你在错误的概念上挣扎时,你会发现你需要添加越来越多的具体事物和规则,才能让事情顺利进行。有了hooks,越来越多的稀奇古怪的东西冒出来,越来越“好用”的hooks帮你做一些琐碎的事情,要学的东西也越来越多。如果我们在日常工作中需要这么多实用程序只是为了隐藏一些奇怪的复杂性,那么这是一个巨大的迹象,表明我们走错了路。几年前,当我从Angular1.5转向React时,我惊讶于React的API是多么简单,文档又是多么薄。Angular曾经有大量的文档,你可能需要几天时间才能涵盖所有内容——消化机制、不同的编译阶段、嵌入、绑定、模板等。仅这一点就给了我很大的启发,而React是如此简洁,你可以去在几个小时内完成整个文档。在第一次、第二次和所有后续尝试使用hooks的过程中,我发现自己不得不一次又一次地使用文档。总结我讨厌成为派对的搅局者,但我真的认为Hooks可能是React社区发生的第二糟糕的事情(第一名仍然由Redux占据)。它为已经脆弱的生态系统增加了另一个无用的争论,并且不清楚hooks是推荐的使用方式还是只是功能和个人品味的另一个问题。我希望React社区能够觉醒并要求在Funclass和类功能之间取得平衡。我们可以在类中有更好的ContextAPI,我们可以为类提供useEffect之类的东西。如果我们愿意,React应该为我们提供继续使用类的选项,而不是仅仅为了向Funclass添加更多功能而强行杀死它来留下类。另外,早在2017年底,我就发表了一篇文章,标题是《Redux的丑陋面》,现在连Redux的创始人DanAbramov都承认Redux是一个巨大的错误。这只是历史重演吗?时间会证明一切。无论如何,我和我的队友决定暂时坚持使用类,并使用基于Mobx的解决方案作为状态管理工具。我认为Hooks在个人开发人员和团队工作人员之间的流行程度存在很大差异-Hooks的不良性质在需要处理其他人代码的大型代码库中更为明显。我个人真的希望React能够将ctrl+z连接在一起。我将开始研究RFC,为React提出一个简单、干净、内置的状态管理解决方案,一劳永逸地解决共享状态逻辑的问题,希望以一种比Funclasses不那么笨拙的方式。