Appearance
反应
本节目标:理解“反应”的核心思路,并能把它用于实际开发或面试表达。
==反应==
==鸣叫====复制链接==
如果您已经使用 React 一段时间了,您可能会遇到容器和展示组件,或者智能和愚蠢的组件。这些术语描述了一种将 React 组件的 UI 层与逻辑分开的[模式](https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0)。
将 UI 与业务逻辑分离并不是 React 所独有的:关注点分离是一个[在 70 年代](https://en.wikipedia.org/wiki/Separation_of_concerns)就[已经存在](https://en.wikipedia.org/wiki/Separation_of_concerns)的设计原则。例如,通常的做法是将访问数据库的代码与后端的业务逻辑分开。
所以在 React 中,我们通过创建包含所有逻辑的容器组件来解决这个问题,然后通过 props 将数据传递给展示组件。
随着 React 钩子的引入,有一种新方法:使用自定义钩子。
为什么要把逻辑和组件分开?
在我们开始将逻辑与 React 组件解耦之前,我们应该知道原因。
以每个函数或组件只负责一件事的方式组织我们的代码,其优点是更容易更改和维护(Dave 和 Andrew在他们的著作_The Pragmatic Programmer 中_称之为“[正交性](https://felixgerschau.com/pragmatic-programmer-20th-anniversary-favorite-topic-summary/#orthogonality)” )。
将此应用于 React 意味着我们的组件看起来更干净、更有条理。例如,我们不需要在编辑 UI 之前滚动逻辑墙。
像这样组织你的代码不仅让它看起来更好、更容易导航,而且还使更改更容易,因为更改钩子不会影响 UI,反之亦然。
测试也更容易访问:如果需要,我们可以独立于 UI 测试逻辑。然而,对我来说最重要的优势是这种方法如何组织我的代码。
如何使用 React 钩子解耦逻辑
为了将逻辑与我们的组件分离,我们将首先创建一个自定义钩子。
我们以这个组件为例。它计算基数和指数的指数值:
256.00
**您可以在**[此处](https://codepen.io/fgerschau/pen/OJmMYoG)**找到完整的源代码。**
代码如下所示:
==export====const====ExponentCalculator=========()=====>===={====const====[====base====,====setBase====]=========useState====(====4====);====const====[====exponent====,====setExponent====]=========useState====(====4====);====const====result== =======(====base== ==**====exponent====)====.====toFixed====(====2====);====const====handleBaseChange=========(====e====)=====>===={====e====.====preventDefault====();====setBase====(====e====.====target====.====value====)====;};====const====handleExponentChange=========(====e====)=====>===={====e====.====preventDefault====();====setExponent====(====e====.====target====.====value====)====;};====return====(====<====div className========="blue-wrapper"====><====input====== ==type========="number"====className========="base"====onChange========={====handleBaseChange====}====placeholder========="Base"====value========={====base====}====/><====input====== ==type========="number"====className========="exponent"====onChange========={====handleExponentChange====}====placeholder========="Exp."====value========={====exponent====}====/><====h1 className========="result"====>===={====result====}====</====h1====>====</====div====>====);};==
这可能看起来已经很好了,但为了本教程,_想象一下_这里有更多的逻辑。
作为第一步,我们将把逻辑移动到一个自定义钩子并在我们的组件中调用它。
==const====useExponentCalculator=========()=====>===={====const====[====base====,====setBase====]=========useState====(====4====);====const====[====exponent====,====setExponent====]=========useState====(====4====);====const====result== =======(====base== ==**====exponent====)====.====toFixed====(====2====);====const====handleBaseChange=========(====e====)=====>===={====e====.====preventDefault====();====setBase====(====e====.====target====.====value====)====;};====const====handleExponentChange=========(====e====)=====>===={====e====.====preventDefault====();====setExponent====(====e====.====target====.====value====)====;};====return===={====base====,====exponent====,====result====,====handleBaseChange====,====handleExponentChange====,====};};====exportconst====ExponentCalculator=========()=====>===={====const===={====base====,====exponent====,====result====,====handleExponentChange====,====handleBaseChange====,====}=========useExponentCalculator====();====// ...====};==
我们可以将这个钩子移到一个单独的文件中,以便更突出地分离关注点。
此外,我们可以进一步将我们的钩子分成更小的、可重用的函数。在这种情况下,我们只能提取calculateExponent.
**使用指数计算器.js**
==const====calculateExponent=========(====base====,====exponent====)=====>====base== ==**====exponent====;====const====useExponentCalculator=========()=====>===={====const====[====base====,====setBase====]=========useState====(====4====);====const====[====exponent====,====setExponent====]=========useState====(====4====);====const====result== =======calculateExponent====(====base====,====exponent====)====.====toFixed====(====2====);====// ...====};==
测试这些函数比测试第一个示例中的整个组件的代码要容易得多。我们可以使用任何 Node.js 测试库来测试它们,甚至不需要支持 React 组件。
我们现在在组件和钩子的代码中拥有特定于框架的代码 (React),而我们的业务逻辑存在于我们稍后定义的不同功能中(与框架无关)。
最佳实践
命名
我喜欢在组件之后命名我的自定义钩子作为use和组件名称(例如useExponentCalculator)的串联。然后我调用与钩子相同的文件。
您可能希望遵循不同的命名约定,但我建议在您的项目中保持一致。
如果我可以重用自定义钩子的一部分,我通常会将它移到src/hooks.
不要过度
尽量务实。如果一个组件只有几行JS,就没有必要分离逻辑。
CSS-in-JS
如果您使用的是 CSS-in-JS 库 ( useStyles),您可能还想将此代码移动到另一个文件中。
您可以将其移动到与钩子相同的文件中。但是,我更喜欢将其保留在同一文件中的组件上方,或者如果它变得太大,则将其移动到自己的文件中。
结论
无论您是否认为使用自定义钩子可以改进您的代码,最终归结为个人喜好。如果您的代码库不包含很多逻辑,则这种模式的优势对您来说不会太重要。
自定义钩子只是增加模块化的一种方式;我还强烈建议在可能的情况下将组件和功能拆分为更小的、可重用的块。
该主题还在The Pragmatic Programmer 中进行了更一般的讨论。我写了一篇文章,涵盖了本书中我最喜欢的主题,因此,如果您对此感兴趣,请务必[查看](https://felixgerschau.com/pragmatic-programmer-20th-anniversary-favorite-topic-summary/)。
链接
以下是我在研究本文时发现有帮助的一些文章:[使用自定义 React 钩子分离关注点](https://areknawo.com/separation-of-concerns-with-custom-react-hooks/)- Arek Nawo展示和容器组件- Dan Abramov一篇关于容器模式的优秀文章,对于 React 中的类组件还是很有用的。
我写的其他一些你可能会喜欢阅读的文章:没有库的 React Hooks 表单验证:完整指南我写这篇文章是为了指导您在不使用任何库的情况下创建自己的表单验证。我发现对于我的用例来说,一个库通常太多了,创建我自己的库只需要几行代码。
如何使您的 React 应用程序成为渐进式 Web 应用程序 (PWA)关于将 React 应用程序转变为 PWA 所需采取的所有步骤的详细指南;这比大多数人想象的要容易。 > 来自 <https://felixgerschau.com/react-hooks-separation-of-concerns/>
