钩子是composing behaviour and state的一种方式,而JSX/组件本质上是应用程序的更高级别的宏视图.
它有助于理解react 本身的进程,从而了解你所描述的模式最初产生的原因.乍一看,您实际上可能认为较旧的类组件在描述您想要做的事情时更简洁,但它们本身也不太灵活.
在钩子出现之前,您已经有了类组件.在类组件中,组件本身是用于状态、效果(也称为生命周期)和表示的抽象.所有这些都倾向于结合在一个单元中.固有地将生命周期/状态与组件耦合的问题在于,生命周期和状态本质上耦合到表示层,因此逻辑在应用程序的其他地方变得更难重用.您可能经常会发现,虽然状态的范围现在只对JSX的一小部分有用,但后来,随着应用程序的增长,您会发现其他一些东西需要访问它,您需要移动状态(及其关联的生命周期行为).
出现了许多模式,在类组件上解决了这个问题,比如HOC和渲染props (值得搜索一下这些),但这些模式有不受欢迎的API,而且有些模式有根本的缺陷,使得组合行为比现在使用钩子时更笨拙.不是说它们不好,实际上它们在特定情况下仍然经常有一席之地,特别是渲染props .
类组件的另一个方面是,用于访问类组件的生命周期的API往往意味着,最终,围绕组件有多个功能需求拆分,而它们实际上应该按用途/功能进行分组.钩子使消费者能够挑选他们想要在一个整洁的重用单元中使用的库功能的哪一部分.
此外,将钩子分开意味着消费者更容易……将某个库的逻辑挂接到他们 Select 的表示风格/JSX树中,而不会固有地与其他人的样式/JSX树相关联.表单库就是一个很好的例子(请参阅Reaction-Hook-Form).
这就产生了钩子.简而言之,钩子的意思是:
- 随着您的应用程序变得更大、更复杂,重用和移动状态和该状态的行为就变得更容易.
- 挂钩使得真正专注于一个功能领域的重用单元变得更容易.
在使用您的示例时,虽然您可能认为nowNothing需要访问钩子的作用域,但如果这个假设是被加入的(就像类组件一样),那么当您决定新的does实际上需要该状态时,重构并在稍后拆分真的很烦人.钩子确保状态和行为保持可移植和可组合.但是,如果这段代码的级别是application,那么实际上可以从useful开始进行这些假设,并创建将它们耦合在一起的包装器.如果您不是在许多未知的上下文中创作高度可重用的东西,那么这个假设通常是安全的.
如果您确信消费者永远不会孤立地关心钩子的作用域,那么真正的答案就是简单地创建一个组件,该组件包装钩子和内部组件,该组件is是API.然而,在库中,这有时可能是一个大错误,因为如果用户的用户区域中潜在的"更高"的其他东西需要访问该状态,库用户基本上是被困住的,除非他们使用肮脏的复制技巧,这会固有地增加应用程序中的错误的表面积.
也就是说,许多库将公开易于使用的组件包装器,这些组件包装器保持内部组件的状态,并单独公开底层挂钩和内部组件,以实现易用性,同时还支持可能需要与该状态的复杂关系的高级用户,这些关系分布在他们 Select 的代码库的多个区域中.
正如我在其他地方对这个问题所做的 comments ,从钩子返回组件不是一个好主意.使用这种方法,您基本上已经在钩子中创建了一个组件(它内在地耦合了钩子和其他内部组件),而不仅仅是使用一个组件来完成这项工作.这样的钩子的行为本质上是与其表示的内部组件分不开的,类组件的问题开始重新出现.这通常会导致作用域和性能噩梦,而且很难进行推理.