关于 presentational-container-pattern
为 React 开发者提供 Container/Presentational 模式指南,将数据与状态处理和界面渲染分开。说明可复用、易测试的优点,也指出小型组件中应避免过度拆分,并介绍 Hooks 作为替代方案。
主要功能
- 01分离数据逻辑与界面渲染
- 02通过 props 传递数据
- 03强调展示组件的复用与测试
- 04说明何时采用或避免该模式
- 05介绍自定义 Hooks 替代方案
适用场景
- 01拆分数据获取和 UI 渲染
- 02构建可复用的展示组件
- 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 张狗狗图片并将它们显示在屏幕上。
理想情况下,我们希望通过将这个过程拆分为两部分来实现关注点分离:
- 展示组件:关注如何向用户展示数据。在这个示例中,就是_渲染狗狗图片列表_。
- 容器组件:关注向用户展示什么数据。在这个示例中,就是_获取狗狗图片_。
获取狗狗图片涉及应用逻辑,而显示图片只涉及视图。
展示组件
展示组件通过 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 钩子,我们仍然可以采用容器/展示组件模式,但在规模较小的应用中,这种模式可能会显得过于复杂。