mcpField
P
presentational-container-pattern

presentational-container-pattern

github.com/patternsdev开发2571.1k 次安装28 周前更新
代码仓库

关于 presentational-container-pattern

为 React 开发者提供 Container/Presentational 模式指南,将数据与状态处理和界面渲染分开。说明可复用、易测试的优点,也指出小型组件中应避免过度拆分,并介绍 Hooks 作为替代方案。

主要功能

  1. 01分离数据逻辑与界面渲染
  2. 02通过 props 传递数据
  3. 03强调展示组件的复用与测试
  4. 04说明何时采用或避免该模式
  5. 05介绍自定义 Hooks 替代方案

适用场景

  1. 01拆分数据获取和 UI 渲染
  2. 02构建可复用的展示组件
  3. 03评估小型组件是否值得拆分

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/patternsdev/skills --skill presentational-container-pattern

安装后在 Claude Code 中输入/presentational-container-pattern确认已加载。

详细介绍

容器/展示模式

在 React 中,实现关注点分离的一种方式是使用 容器/展示模式。通过这种模式,我们可以将视图与应用逻辑分离。

适用场景

  • 当你希望清晰地分离数据获取逻辑与 UI 渲染时,使用此模式
  • 这有助于让展示组件更易于复用和测试

不适用场景

  • 对于小型组件,将其拆分到两个文件中会增加额外开销,却没有明显收益
  • 当 Hooks 已封装数据逻辑时,单独的容器组件会显得多余
  • 当组件是一次性视图,且任一层都没有复用可能时

说明

  • 容器组件负责数据获取和状态管理;展示组件负责渲染
  • 在现代 React 中,为实现相同的关注点分离,优先使用自定义 Hooks,而不是容器组件
  • 让展示组件保持为纯函数,通过 props 接收数据
  • 仅在此模式确实能简化架构时使用——小型组件应避免使用

详情

假设我们要创建一个应用,获取 6 张狗狗图片并将它们显示在屏幕上。

理想情况下,我们希望通过将这个过程拆分为两部分来实现关注点分离:

  1. 展示组件:关注如何向用户展示数据。在这个示例中,就是_渲染狗狗图片列表_。
  2. 容器组件:关注向用户展示什么数据。在这个示例中,就是_获取狗狗图片_。

获取狗狗图片涉及应用逻辑,而显示图片只涉及视图。

展示组件

展示组件通过 props 接收数据。它的主要功能只是按照我们希望的方式显示接收到的数据,包括样式,_而不修改_这些数据。

来看一个显示狗狗图片的示例。渲染狗狗图片时,我们只需遍历从 API 获取的每张狗狗图片,并渲染这些图片。为此,我们可以创建一个函数组件,通过 props 接收数据,并渲染收到的数据。

DogImages 组件是一个展示组件。展示组件_通常_是无状态的:它们不包含自己的 React 状态,除非出于 UI 需要状态。展示组件本身不会修改接收到的数据。

展示组件从容器组件接收数据。

容器组件

容器组件的主要功能是向其_包含_的展示组件传递数据。容器组件本身通常不会渲染其他组件,只会渲染负责展示其数据的展示组件。由于它们本身不渲染内容,因此通常也不包含任何样式。

在我们的示例中,我们希望将狗狗图片传递给 DogsImages 展示组件。要做到这一点,我们需要先从外部 API 获取图片。我们需要创建一个容器组件来获取这些数据,并将数据传递给展示组件 DogImages,以便在屏幕上显示。

将这两个组件组合起来,就可以把应用逻辑处理与视图分离。

钩子

在许多情况下,可以用 React 钩子替代容器/展示组件模式。钩子的引入让开发者可以轻松添加状态,而无需使用容器组件来提供状态。

我们可以创建一个自定义钩子来获取图像并返回狗狗数组,而不是将数据获取逻辑放在 DogImagesContainer 组件中。

export default function useDogImages() {
  const [dogs, setDogs] = useState([]);

  useEffect(() => {
    fetch("https://dog.ceo/api/breed/labrador/images/random/6")
      .then((res) => res.json())
      .then(({ message }) => setDogs(message));
  }, []);

  return dogs;
}

使用这个钩子后,我们不再需要用外层的 DogImagesContainer 容器组件来获取数据并将其传递给展示组件 DogImages。相反,我们可以直接在展示组件 DogImages 中使用这个钩子!

使用 useDogImages 钩子,我们仍然将应用逻辑与视图分离。我们只是使用 useDogImages 钩子返回的数据,并不在 DogImages 组件中修改这些数据。

钩子可以像容器/展示组件模式一样,轻松地将组件中的逻辑与视图分离。它省去了额外的层级,因此无需再用容器组件包装展示组件。

优点

使用容器/展示组件模式有许多好处。

容器/展示组件模式有助于分离关注点。展示组件可以是只负责 UI 的纯函数,而容器组件则负责应用的状态和数据。这使得分离关注点更容易落实。

展示组件很容易复用,因为它们只会 显示 数据,而不会修改数据。我们可以在应用的不同场景中复用展示组件。

由于展示组件不会改变应用逻辑,不熟悉代码库的人也可以轻松调整展示组件的外观,例如设计师。如果展示组件在应用的多个位置复用,这些更改可以在整个应用中保持一致。

展示组件通常是纯函数,因此很容易测试。根据传入的数据,我们就能知道组件会渲染什么,无需模拟数据存储。

缺点

容器/展示组件模式便于将应用逻辑与渲染逻辑分离。不过,使用钩子也能实现同样的效果,无需采用容器/展示组件模式,也无需将无状态函数组件重写为类组件。请注意,如今我们不再需要创建类组件来使用状态。

虽然即使使用 React 钩子,我们仍然可以采用容器/展示组件模式,但在规模较小的应用中,这种模式可能会显得过于复杂。

来源

参考资料

评论

支持 Markdown