这是历史遗留问题,所以不建议使用(现在有了Hooks),在这里只是让我们了解这段历史
在 React 中,一种强制分离关注点的方法是通过使用容器/演示模式。使用这种模式,我们可以将视图与应用程序逻辑分开。
在 React 中,强制分离关注点的一种方法是使用 Container/Presentational
模式。 使用这种模式,我们可以将视图与应用程序逻辑分开。
假设我们要创建一个应用程序来获取 6 个狗的图像,并将这些图像呈现在屏幕上。
理想情况下,我们希望通过将此过程分为两部分来强制分离关注点:
- 展示组件:关心如何向用户显示数据的组件。 在这个例子中,这是渲染狗图像列表。
- 容器组件:关心向用户显示什么数据的组件。 在这个例子中,这是获取狗的图像。
容器组件 获取狗图像处理应用程序逻辑,而 展示组件 只处理视图。
展示组件
一个展示组件通过 props 接收它的数据。
它的主要功能是简单地以我们希望的方式显示它接收到的数据,包括样式,而不修改该数据。
让我们看一下显示狗图像的示例。
在渲染狗图像时,我们只想映射从 API 获取的每个狗图像,并渲染这些图像。
为此,可以创建一个功能组件,通过 props 接收数据,并渲染它接收到的数据。
1
2
3
4
5
|
//展示组件 DogImages.js
export default function DogImages({ dogs }) {
return dogs.map((dog, i) => <img src={dog} key={i} alt="Dog" />);
}
|
容器组件
容器组件的主要功能是将数据传递给它们包含的表示组件。
除了关心其数据的展示组件之外,容器组件本身通常不会渲染任何其他组件。
因为它们自己不渲染任何东西,所以它们通常也不包含任何样式。
在示例中,我们希望将狗图像传递给 DogsImages 展示组件。
在能够这样做之前,需要从外部 API 获取图像。
需要创建一个容器组件来获取这些数据,并将这些数据传递给展示组件 DogImages 以便在屏幕上显示它。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
//容器组件 DogImagesContainer.js
import React from "react";
import DogImages from "./DogImages";
export default class DogImagesContainer extends React.Component {
constructor() {
super();
this.state = {
dogs: []
};
}
componentDidMount() {
fetch("https://dog.ceo/api/breed/labrador/images/random/6")
.then(res => res.json())
.then(({ message }) => this.setState({ dogs: message }));
}
render() {
return <DogImages dogs={this.state.dogs} />;
}
}
|
将组件渲染到页面中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
//将组件渲染到页面中
import React from "react";
import { render } from "react-dom";
import DogImagesContainer from "./DogImagesContainer";
import "./styles.css";
function App() {
return (
<div className="App">
<h1>
Browse Dog Images{" "}
<span role="img" aria-label="emoji">
🐕
</span>
</h1>
<DogImagesContainer />
</div>
);
}
render(<App />, document.getElementById("root"));
|
Hooks
在很多情况下,Container/Presentational 模式可以用 React Hooks 代替。
Hooks 的引入使开发人员可以轻松添加状态,而无需容器组件来提供该状态。
我们可以创建一个自定义钩子来获取图像并返回狗的数组,而不是在 DogImagesContainer 组件中使用数据获取逻辑。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
//自定义hook useDogImages.js
import { useState, useEffect } from "react";
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 组件中使用这个钩子!
1
2
3
4
5
6
7
8
9
|
//展示组件 DogImages.js
import useDogImages from "./useDogImages";
export default function DogImages() {
const dogs = useDogImages();
return dogs.map((dog, i) => <img src={dog} key={i} alt="Dog" />);
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
//将展示组件渲染到页面
import React from "react";
import { render } from "react-dom";
import DogImages from "./DogImages";
import "./styles.css";
function App() {
return (
<div className="App">
<h1>
Browse Dog Images{" "}
<span role="img" aria-label="emoji">
🐕
</span>
</h1>
<DogImages />
</div>
);
}
render(<App />, document.getElementById("root"));
|
通过使用 useDogImages 钩子,我们仍然将应用程序逻辑与视图分离。
我们只是使用从 useDogImages 钩子返回的数据,而没有在 DogImages 组件中修改该数据。
钩子可以很容易地分离组件中的逻辑和视图,就像容器/展示模式一样。 它为我们节省了将展示组件包装在容器组件中所需的额外层。
优点
使用容器/展示模式有很多好处。
Container/Presentational 模式鼓励关注点分离。
展示组件可以是负责 UI 的纯函数,而容器组件则负责应用程序的状态和数据。这使得实施关注点分离变得容易。
展示组件很容易重用,因为它们只是显示数据而不改变这些数据。可以为不同的目的在整个应用程序中重用展示组件。
由于展示组件不会改变应用程序逻辑,因此不了解代码库的人(例如设计人员)可以轻松更改表示组件的外观。
如果在应用程序的许多部分重用了展示组件,则更改可以在整个应用程序中保持一致。
测试展示组件很容易,因为它们通常是纯函数。我们知道组件将根据我们传递的数据呈现什么,而无需模拟数据存储。
缺点
Container/Presentational 模式可以轻松地将应用程序逻辑与呈现逻辑分开。
但是,Hooks 可以实现相同的结果,而无需使用容器/展示模式,也无需将无状态功能组件重写为类组件。
尽管我们仍然可以使用容器/展示模式,甚至使用 React Hooks,但这种模式在较小规模的应用程序中很容易成为一种矫枉过正。
知识点