Паттерн «Контейнер-Презентатор»
(Container-Presenter) позволяет разделять логику управления состоянием и
визуальное представление компонентов. В контексте
Hyperapp это особенно актуально, так как фреймворк по
своей природе минималистичен и концентрируется на чистой функции
view и одном глобальном state.
Контейнерный компонент отвечает за:
Контейнерные компоненты не содержат JSX-разметки и элементов UI, их задача — подготовить данные и действия для дочерних компонентов.
Пример контейнера в Hyperapp:
import { h } from "hyperapp";
import TodoList from "./TodoList";
const TodoContainer = ({ state, actions }) => {
const addTodo = (text) => {
actions.addTodo({ id: Date.now(), text });
};
const removeTodo = (id) => {
actions.removeTodo(id);
};
return h(TodoList, {
todos: state.todos,
addTodo,
removeTodo
});
};
export default TodoContainer;
Здесь контейнер передаёт данные и функции управления состоянием в
компонент-презентатор TodoList.
Презентаторные компоненты сосредоточены исключительно на отображении интерфейса. Они:
Пример презентатора:
import { h } from "hyperapp";
const TodoList = ({ todos, addTodo, removeTodo }) => {
let input;
return h("div", {}, [
h("ul", {}, todos.map(todo =>
h("li", {}, [
todo.text,
h("button", { onclick: () => removeTodo(todo.id) }, "Удалить")
])
)),
h("input", { oninput: e => input = e.target.value }),
h("button", { onclick: () => addTodo(input) }, "Добавить")
]);
};
export default TodoList;
Презентатор полностью отделён от логики приложения: он только отображает список задач и вызывает переданные функции.
Главная цель паттерна — минимизировать зависимость UI от состояния приложения. Контейнер управляет state и действиями, а презентатор — отрисовкой. Это обеспечивает:
В Hyperapp actions представляют собой функции,
изменяющие состояние. Контейнеры могут агрегировать несколько действий и
передавать их презентаторам. Например:
const actions = {
addTodo: (todo) => (state) => ({ todos: [...state.todos, todo] }),
removeTodo: (id) => (state) => ({ todos: state.todos.filter(t => t.id !== id) })
};
const state = { todos: [] };
Контейнер получает state и actions и
формирует пропсы для презентатора. Презентатор остаётся чистым UI.
Контейнер может объединять несколько презентаторов, создавая сложные интерфейсы из маленьких компонентов:
const AppContainer = ({ state, actions }) => {
return h("div", {}, [
h(Header, { title: "Список задач" }),
h(TodoContainer, { state, actions }),
h(Footer, {})
]);
};
Такой подход сохраняет чистоту и модульность интерфейса.
Контейнеры удобны для работы с асинхронными запросами:
const TodoContainer = ({ state, actions }) => {
const fetchTodos = async () => {
const response = await fetch("/api/todos");
const data = await response.json();
actions.setTodos(data);
};
return h(TodoList, {
todos: state.todos,
fetchTodos
});
};
Презентатор не знает о fetch, он просто вызывает
переданную функцию.
Паттерн «Контейнер-Презентатор» в Hyperapp превращает минималистичный фреймворк в гибкий инструмент для построения сложных, масштабируемых приложений, где логика и визуальное представление остаются независимыми и легко тестируемыми.