🎣 React Hooks:从 mixin 到 hooks 的旅程
由 Mux 赞助的 DEV 全球展示挑战赛:展示你的项目!
简要介绍
除非你与世隔绝(我不确定这样会不会舒服😂),否则你肯定听说过钩子,也知道它们在音乐圈里是如何迅速走红的。
人们很容易被各种炒作所迷惑,而忽略了我们是如何走到今天这一步的,以及背后的原因。如果你还没看过 Dan 在 2018 年 ReactConf 大会上做的那个视频,我建议你快速浏览一下。那个视频解释了 Hooks 的作用以及它们诞生的原因。
我们是怎么走到这一步的?
React 提供了两种开发组件的方式:类和函数。讽刺的是,直到最近,JavaScript 社区还在抱怨为什么 JavaScript 没有类,甚至还开发了类似createClass`/etc` 这样的框架。而最近,社区却来了个180度大转弯,全面转向函数式编程。这是为什么呢?或许是因为人类总是想要得不到的东西,又或许是因为我们意识到,即使得到了想要的东西,它们也并不符合 JavaScript 的典型继承模型。
在 Hooks 推出之前,函数式组件开发方法仅限于非常基础的组件,因为无法利用状态或生命周期函数。当我第一次看到 Hooks 时,脑海中浮现出 Angular 1.x 代码中那些庞大的函数。诚然,这绝对不可取,但说实话,在实际应用中,这种情况迟早会发生。我个人更喜欢类方法,因为它能保持代码的井然有序,所以一开始我对函数式方法并不感冒,但用得越多,我就越喜欢它。
历史时刻!
随着我使用鱼钩的次数越来越多,我开始越来越喜欢它们。有一天,当我正在使用鱼钩时,我突然想到,我们是怎么走到这一步的?当我开始思考这个问题时,我发现这里还有一段相当长的历史。
问题很简单:如何在多个组件之间共享代码?毕竟,代码重用是我们开始编写代码时最先学习的内容之一。这通常是指处理诸如窗口大小调整事件、滚动事件等通用事件的代码。难点在于如何尽可能清晰地实现代码重用,避免出现任何“魔法”式的调用。
指令
在 Angular 中,指令的概念允许你为元素添加一些功能。例如,我可以这样做:
<div
[appWindowResize]="myBreakpoints"
(windowWidthChanged)="setNewSize($event)">
</div>
我刚开始写 React 的时候,这是我最怀念的功能之一。这种方法最大的问题之一就是很难用共享作用域将这些组件串联起来。
混合料
早期版本的 React 使用了一种createClass方法,并且引入了 mixin 的概念。不久前,React 发布了一篇名为《Mixins 有害论》的文章。这篇文章的核心观点是,随着组件中 mixin 数量的增加,其运行机制会变得“难以理解”。一个包含 mixin 的组件可能如下所示:
var Button = React.createClass({
mixins: [WindowReisze, Orientation, Animation, Tap, Drag]
});
在这个组件的主体中,现在突然出现了这么多方法。这些方法从何而来?如果它们名称重叠怎么办?等等。
除此之外,React 更倾向于函数式编程,所以这种createClass方法并不太适合这种情况。
装饰师
一旦 JavaScript 引入了类,我们就立即开始引入其他语言(例如 C#)的概念。Angular 在 Angular 2.x 中更加强化了这种方法,使整个框架都由装饰器驱动。
@Component({ ... })
export class MyComponent {
@Input() name: string;
}
说实话,我并不反对装饰器,但我们还没来得及完善类,就开始添加其他语言特性,结果不得不做出改变。现在,所有严重依赖装饰器的代码可能都得重新设计了。
React 装饰器的问题和我上面提到的问题一样,React 倾向于函数式编程,而当你把装饰器应用到函数上时,最终代码会变成这样:
@bind
@resize
function bar() { ... }
于是,这件事就变得难以理解了。
高阶分量(HOC)
接下来出现了高阶组件。它们提供了与装饰器类似的功能,但不需要新的语言特性。然而,它们也存在与装饰器相同的问题:难以理解它们的运行机制。
export default withRouter(
connect<{}, {}, {}, DashboardProps>(
mapStateToProps,
mapDispatchToProps
)(Dashboard)
);
在上面的例子中,只有两个 HOC 连接在一起,但我已经无法告诉你到底发生了什么。
渲染函数
当我们意识到所有这些高阶方法都存在难以理解的共同问题时,社区提出了“渲染函数”方法。虽然这种方法更加明确,并且倾向于一种对 Web 开发来说更自然、更具声明性的方式,但它很快就会变得难以控制。
<Route>
{route => (
<Permissions>
{roles => (
<Query variables={{ foo: true }}>
{data => (
<Dashboard {...data} {...route} {...roles} />
)}
</Query>
)}
</Permissions>
)}
</Route>
这种方法更加明确,但也需要付出代价。
总结一下……
正如你所见,所有这些方法都有其代价。Hooks 为我们提供了一种全新的视角来构建可组合组件,并实现高代码复用,但很难预测在大型代码库中,当多个开发人员对同一段代码进行修改后,它们在实际应用中会如何发挥作用。
希望您喜欢这篇文章,如果您喜欢,请在Twitter和Github上关注我,获取更多 JavaScript 技巧/观点/项目/文章等!
文章来源:https://dev.to/amcdnl/react-hooks-the-journey-of-mixins-to-hooks-2jch