将应用程序的组件分为展示(木偶)组件和容器(智能)组件。

展示组件

  • 主要关注UI,它们负责组件的外观。
  • 数据由 props 提供,木偶组件中不应该调用API,这是智能组件的工作
  • 除了UI的依赖包,它们不需要依赖应用程序
  • 它们可能包括状态,但仅用于操纵UI本身-它们不应存储应用程序数据。

木偶组件有:加载指示器,模态,按钮,输入。

容器组件

  • 它们不关注样式,通常不包含任何样式
  • 它们用于处理数据,可以请求数据,捕获更改和传递应用程序数据
  • 负责管理状态,重新渲染组件等等
  • 可能依赖于应用程序,调用 Redux,生命周期方法,API和库等等。

使用展示组件和容器组件的好处

  • 更好的可读性
  • 更好的可重用性
  • 更容易测试

此外,它还符合“单一责任原则” - 一个组件负责外观,另一个组件负责数据。
让我们看一个简单的例子。这是一个 BookList 组件,该组件可从API获取图书数据并将其显示在列表中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import React, { useState, useEffect } from 'react';

const BookList = () => {
const [books, setBooks] = useState([]);
const [isLoading, setLoading] = useState(false);

useEffect(() => {
setLoading(true);
fetch('api/books')
.then(res => res.json())
.then(books => {
setBooks(books);
setLoading(false);
});
}, []);

const renderLoading = () => {
return <p>Loading...</p>;
};

const renderBooks = () => {
return (
<ul>
{books.map(book => (
<li>{book.name}</li>
))}
</ul>
);
};

return <>{isLoading ? renderLoading() : renderBooks()}</>;
};
export default BookList;

该组件的问题在于,它负责太多事情。它获取并呈现数据。它还与一个特定的接口关联,因此在不复制代码的情况下,不能使用此组件显示特定用户的图书列表。

现在,让我们尝试将此组件分为展示组件和容器组件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import React from 'react';

const BookList = ({ books, isLoading }) => {
const renderLoading = () => {
return <p>Loading...</p>;
};

const renderBooks = () => {
return (
<ul>
{books.map(book => (
<li key={book.id}>{book.name}</li>
))}
</ul>
);
};

return <>{isLoading ? renderLoading() : renderBooks()}</>;
};
export default BookList;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import React, { useState, useEffect } from 'react';
import BookList from './BookList';

const BookListContainer = () => {
const [books, setBooks] = useState([]);
const [isLoading, setLoading] = useState(false);

useEffect(() => {
setLoading(true);
fetch('/api/books')
.then(res => res.json())
.then(books => {
setBooks(books);
setLoading(false);
});
}, []);

return <BookList books={books} isLoading={isLoading} />;
};

export default BookListContainer;

如你所见,它看起来要好得多。更重要的是,它使我们可以在具有不同数据的许多地方使用 BookList 组件。