Що таке "Container-Presentational Pattern"?
Суть патерну
Container-Presentational Pattern ділить компоненти на два типи:
| Тип | Призначення |
|---|---|
| Container (контейнер) | Відповідає за логіку, дані та стан |
| Presentational (презентаційний) | Відповідає лише за відображення UI |
Ідея: "контейнер думає, а презентаційний показує"
Приклад
Presentational Component
Він отримує дані через props і просто їх відображає.
javascript
function UserCard({ name, age }) {
return (
<div className="card">
<h2>{name}</h2>
<p>Вік: {age}</p>
</div>
);
}Не містить стану Не робить запитів Не знає, звідки приходять дані → чистий UI-компонент
Container Component
Він керує станом і логікою та передає дані вниз.
javascript
function UserCardContainer() {
const [user, setUser] = useState(null);
useEffect(() => {
fetch('/api/user')
.then(res => res.json())
.then(data => setUser(data));
}, []);
if (!user) return <p>Завантаження...</p>;
return <UserCard name={user.name} age={user.age} />;
}Отримує дані Керує станом Викликає побічні ефекти Передає дані в презентаційний компонент
Навіщо це потрібно
| Проблема | Рішення через патерн |
|---|---|
| Логіка і UI змішані в одному компоненті → важко читати | Розділяємо: один думає, інший показує |
| Важко тестувати і повторно використовувати | Презентаційні легко тестувати й використовувати повторно |
| Потрібно відобразити той самий UI з різними джерелами даних | Можна використати один Presentational, а різні Container |
| Під час редизайну ламається логіка | UI можна замінити без зміни бізнес-логіки |
Архітектурно
javascript
Container (логіка)
└── Presentational (UI)Наприклад:
javascript
UserListContainer
└── UserList
└── UserCardТипові ознаки
| Ознака | Container | Presentational |
|---|---|---|
| Тримає стан | Так | Ні |
Використовує хуки (useState, useEffect) | Так | Рідко |
| Робить запити до API | Так | Ні |
| Знає про бізнес-логіку | Так | Ні |
| Приймає props для відображення | Так | Так |
| Визначає розмітку і стилі | Ні | Так |
Приклад у сучасній архітектурі (React + Redux)
javascript
// Presentational.jsx
export function TodoList({ todos, onToggle }) {
return (
<ul>
{todos.map(todo => (
<li
key={todo.id}
onClick={() => onToggle(todo.id)}
style={{ textDecoration: todo.done ? 'line-through' : 'none' }}
>
{todo.text}
</li>
))}
</ul>
);
}javascript
// Container.jsx
import { useSelector, useDispatch } from 'react-redux';
import { toggleTodo } from '../store/todoSlice';
import { TodoList } from './Presentational';
export function TodoListContainer() {
const todos = useSelector(state => state.todos);
const dispatch = useDispatch();
return (
<TodoList
todos={todos}
onToggle={(id) => dispatch(toggleTodo(id))}
/>
);
}У сучасному React
Хоча патерн родом з епохи класових компонентів, він все ще актуальний - просто реалізується через хуки й композицію.
Тепер "контейнером" може бути:
- хук (
useUser,useTodos) - компонент-обгортка
- контекст-провайдер
Підсумок
| Що | Опис |
|---|---|
| Ідея | Розділити "логіку" і "відображення" |
| Container | Отримує й обробляє дані |
| Presentational | Показує інтерфейс |
| Користь | Повторне використання, читабельність, тестованість |
| Сучасний аналог | Хуки + Dumb/UI компоненти |
Підсумкова формула:
Containerзнає що показати,Presentationalзнає як це показати.
Коротка відповідь
Для співбесідиPremium
Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.