前提
每个程序员的梦想都是实现财富自由然后就可以不工作了,但是能实现的都是少数,但是如果在工作中摸鱼赚钱,确实比较好实现的事。
那怎么实现呢?
当然是通过写更少的代码,来实现我们的功能需求啦,并且最好的话,是让所有的代码都能够复用。
你可能会问那要怎么样才能实现代码的复用呢?
当然是正确地将组件的逻辑与其呈现分离开来。
你肯定会说:说起来容易做起来难,对吧?
如何有效地将组件解耦
接下来我们就一起来研究如何有效地将组件解耦,让我们的代码变的复用性极高。
在我们开始之前,让我们看看"耦合"这一基本概念。
耦合
在计算机科学中,耦合是指两个或多个组件之间的依赖关系的概念。例如,如果组件A依赖于另一个组件B,那么就可以说A与B耦合在一起。
耦合是我们提高代码复用率的敌人,因为它会将需要独立修改的组件或部分联系在一起,从而增加了代码维护的难度。这意味着当你需要修改其中一个部分时,可能会不得不同时修改其他部分,这会导致系统更容易出现错误和问题。
所以我们必须花时间去review所有需要进行修改相对应的部分,否则可能会引发很多我们未知的bug。
如果我们将React组件视为一个纯粹的呈现元素,们可以说它可以与很多东西耦合:
- 决定其行为的业务逻辑(hooks、自定义hooks等)。
- 外部服务(API、数据库等)。
- 另一个React组件(例如,负责管理表单状态的组件)。
这种紧密的耦合在修改时可能会对系统的其他部分产生不可预测的副作用。
让我们来看下面这个组件
jsx
import { useCustomerHook } from './hooks';
const Customer = () => {
const { name, surname } = useCustomerHook();
return (
<div>
<p>{name}</p>
<p>{surname}</p>
</div>
);
};
乍一看,好像一切都很好,但实际上,它有一个问题:这个组件与useCustomerHook
耦合在一起,该hook是从外部服务获取客户数据。因此,我们的Customer组件不是一个"纯粹"的组件,因为它依赖于与其UI呈现无关的逻辑。
如果,让我们想要让useCustomerHook
也在其他的组件中使用。那么我们就要对其进行修改,这会让我们修改相当多的地方,工作量会很大,因为我们必须修改所有使用它并与它耦合的组件。
解耦React组件的逻辑
还是刚刚那个例子。Customer组件与自定义的 useCustomerHook耦合,该hook 应用了获取客户数据的逻辑。
那我们应该怎么去解耦这个组件呢?让他变成纯粹的呈现组件。
jsx
import { useCustomerHook } from './hooks';
const Customer = ({name, surname}) => {
return (
<div>
<p>{name}</p>
<p>{surname}</p>
</div>
);
};
const CustomerWrapper = () => {
const { name, surname } = useCustomerHook();
return <Customer name={name} surname={surname} />;
};
export default CustomerWrapper;
我们可以使用了一个包装组件来解耦Customer组件的逻辑。这种技巧被称为容器组件,它允许我们修改组件的UI,而不必担心"破坏"底层逻辑。
现在,我们的Customer只需要关注显示呈现信息。所有必要的变量都作为props传递,它就可以轻松嵌套在我们的代码中的任何位置,而无需担忧代码耦合的问题。
虽然我们已经成功解决,但是它仍然存在两个问题
- CustomerWrapper组件仍与自定义的hoos耦合。因此,如果我想要对它进行修改的话仍然需要修改包装组件。
- 我们必须创建额外的组件CustomerWrapper来解耦逻辑,这意味着我们需要写了更多的代码了。
那我们可以怎么解决呢?我们可以通过使用Composition来解决这两个问题。
Composition
在计算机科学中,Composition是一个概念,指的是将两个或多个元素组合在一起以创建一个新元素。例如,如果我们有两个函数f和g,我们可以将它们组合在一起以创建一个新的函数h,h是f和g的组合。
jsx
const f = (x) => x + 1;
const g = (x) => x * 2;
const h = (x) => f(g(x));
我们也可以将相同的概念应用于自定义hook上。事实上,我们可以将两个或多个自定义hook组合在一起以创建一个新的自定义钩hook。
jsx
const useCustomerHook = () => {
const { name, surname } = useCustomer();
const { age } = useCustomerAge();
return { name, surname, age };
};
通过使用Composition,我们可以在不创建包装组件的情况下解耦React组件的逻辑。为了方便地应用组合,我们可以使用了react-hooks-compose库。
让我们看看如何在我们的示例中应用组合。
jsx
import composeHooks from 'react-hooks-compose';
import { useCustomerHook } from './hooks';
const Customer = ({name, surname}) => {
return (
<div>
<p>{name}</p>
<p>{surname}</p>
</div>
);
};
export default composeHooks({useCustomerHook})(Customer);
现在,Customer组件确实是一个纯粹的呈现组件。它与任何自定义钩子都没有耦合在一起,只处理UI逻辑。此外,您无需创建任何额外的组件来解耦逻辑。事实上,组合使您能够创建更清晰和可读性更强的组件。
这种技术的另一个优点在于它如何简化对Customer组件的测试。您不必担心测试业务逻辑;您只需要测试UI逻辑。此外,您还可以单独测试自定义钩子。
最后,让我们看看如果您决定添加一个新的自定义钩子,用于处理Customer组件的某些逻辑,例如处理客户信息记录的自定义钩子。
jsx
import composeHooks from 'react-hooks-compose';
import { useCustomerHook, useLoggerHook } from './hooks';
const Customer = ({name, surname, age}) => {
return (
<div>
<p>{name}</p>
<p>{surname}</p>
</div>
);
};
export default composeHooks({
useCustomerHook,
useLoggerHook
})(Customer);
总结
在这篇文章中,我们探讨了如何使用hook composition的方法,让React组件的逻辑更容易理解,并将其变成了一种更像是专注于展示的组件。这种hook composition的技巧就像一把有力的工具,可以帮助我们提高React组件的结构和可维护性。
这个方法的核心思想是将业务逻辑和展示层清晰地分隔开,这样我们的组件就更容易阅读和测试。这种方式有助于提高代码的可重用性,并且可以更轻松地扩展React应用,使程序员可以专注于创建更加清晰和性能更佳的组件。