React 进阶:以正确的方式解耦组件

2025-06-04

React 进阶:以正确的方式解耦组件

每个开发人员的梦想都是编写更少的代码,并且如果可能的话,使所有代码都可以重复使用。

在 React 中,这意味着知道如何正确地将组件的逻辑与其呈现分离。

说起来容易做起来难,对吧?

在本文中,我将向您展示如何有效地解耦组件,从而使您的代码具有极高的可复用性。
在开始之前,让我们先了解一下“耦合”的基本概念。

耦合

在计算机科学中,耦合是一个表示两个或多个组件之间依赖关系的概念。例如,如果一个组件A依赖于另一个组件BA则称其与组件耦合B

耦合是变革的敌人,因为它将可以并行改变的事物联系在一起。

这使得修改应用程序中的单个点变得极具挑战性。修改单个组件可能会导致应用程序各个部分出现异常。

您必须花时间找到所有需要修改的部分,否则您会发现自己想知道为什么一切都变得混乱。

如果我们将 React 组件视为纯粹的展示元素,那么我们可以说它可以与许多东西结合:

  • 决定其行为的业务逻辑(钩子、自定义钩子等)。
  • 外部服务(API、数据库等)。
  • 另一个 React 组件(例如,负责管理表单状态的组件)。

这种紧密耦合一旦被修改,可能会对系统的其他部分产生不可预测的副作用。

让我们仔细看看这个组件。

import { useCustomerHook } from './hooks';

const Customer = () => {
  const { name, surname } = useCustomerHook();
  return (
    <div>
      <p>{name}</p>
      <p>{surname}</p>
    </div>
  );
};
Enter fullscreen mode Exit fullscreen mode

乍一看似乎没什么问题,但实际上,我们遇到了一个问题:这个组件与自定义钩子 useCustomerHook 耦合,该钩子从外部服务检索客户数据。因此,我们的 Customer 组件不是一个“纯”组件,因为它依赖的逻辑并非仅仅与呈现其 UI 相关。

现在,假设自定义钩子 useCustomerHook 也在其他组件中使用。如果我们决定修改它,会发生什么?好吧,我们应该做好相当多工作的准备,因为我们必须修改所有使用它并与之耦合的组件。

解耦 React 组件的逻辑

让我们回顾一下前面的例子。我提到过,Customer 组件与自定义钩子 useCustomerHook 耦合,该钩子应用获取逻辑来检索客户数据。

现在我们来探讨一下如何解耦这个组件的逻辑,让它变成一个纯粹的展示组件。

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;
Enter fullscreen mode Exit fullscreen mode

现在,Customer 组件确实是一个纯粹的展示组件,因为它不再使用 useCustomerHook 并且只处理 UI 逻辑。

我使用了一个包装组件来解耦 Customer 组件的逻辑。这种技术被称为容器组件,它允许我们修改组件的 UI,而不必担心“破坏”底层逻辑。

现在,Customer 只需要关心如何显示信息。所有必要的变量都作为 props 传递,因此可以轻松地将其嵌套在代码中的任何位置,无需担心。
但是,我仍然不满意,原因有二:

  1. CustomerWrapper 组件仍然与自定义钩子耦合。因此,如果我决定修改它,我仍然需要修改包装器组件。
  2. 我不得不创建一个额外的组件 CustomerWrapper 来解耦逻辑,这意味着我又写了一点代码。我们可以通过使用组合来解决这两个问题。

作品

在计算机科学中,组合是一个概念,指的是将两个或多个元素组合起来创建一个新的元素。例如,如果我们有两个函数fg,我们可以将它们组合起来创建一个新的函数,这就是h的复合fg

const f = (x) => x + 1;
const g = (x) => x * 2;
const h = (x) => f(g(x));
Enter fullscreen mode Exit fullscreen mode

同样的道理,我们也可以将其应用于自定义 hooks。实际上,我们可以组合两个或多个自定义 hooks 来创建一个新的 hooks。

const useCustomerHook = () => {
  const { name, surname } = useCustomer();
  const { age } = useCustomerAge();
  return { name, surname, age };
};
Enter fullscreen mode Exit fullscreen mode

这样,自定义 hook useCustomerHook 就由自定义 hooks useCustomer 和 useCustomerAge 组成。

通过使用组合,我们可以解耦 React 组件的逻辑,而无需创建包装器组件。为了方便地应用组合,我们使用了react-hooks-compose库。

让我们看看如何将组合应用到我们的例子中。

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);
Enter fullscreen mode Exit fullscreen mode

现在,Customer 组件确实是一个纯粹的展示组件。它没有与任何自定义钩子耦合,只处理 UI 逻辑。此外,您无需创建任何其他组件来解耦逻辑。事实上,组合允许您创建一个更简洁、更易读的组件。

这项技术的另一个优势在于它使 Customer 组件的测试变得非常简单。您无需担心测试业务逻辑,只需测试 UI 逻辑即可。此外,您还可以单独测试自定义钩子。

最后,让我们看看如果您决定添加一个新的自定义钩子,向客户组件添加一些逻辑,例如处理记录客户信息的自定义钩子,会发生什么。

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);
Enter fullscreen mode Exit fullscreen mode

太好了,您已经将自定义钩子 useLoggerHook 添加到 Customer 组件,而无需修改组件本身。

这是因为 useLoggerHook 是由 useCustomerHook 组合而成的。

结论

在本文中,我们探讨了如何通过钩子组合来解耦 React 组件的逻辑,将其转变为纯粹的展示组件。钩子组合的技巧为我们提供了一个强大的工具,可以增强 React 组件的模块化和可维护性。

通过将业务逻辑与展现逻辑清晰地分离,我们使组件更易于阅读和测试。这种方法提升了代码的可重用性和 React 应用程序的可扩展性,使开发人员能够专注于创建更简洁、更高效的组件。

如果您对此主题有任何建议或疑问,请在下方评论区留言。如果您觉得本文对您有帮助,别忘了与您的开发者同行分享!

文章来源:https://dev.to/argonauta/react-advance-decoupling-your-components-in-the-right-way-4pkn
PREV
成为区块链开发者,你需要具备以下条件
NEXT
与开发影响者一起分享最简单的努力