Container Pattern в Haunted представляет собой подход к организации компонентов, при котором логика управления состоянием и данные отделяются от представления. Этот паттерн широко используется для повышения масштабируемости и тестируемости приложений на Web Components с использованием Haunted.
Container обычно выполняет три основные функции:
Управление состоянием Container отвечает за
хранение и обновление данных, которые используются
компонентами-представлениями. В Haunted это чаще всего реализуется с
помощью хука useState или useReducer.
Связь с внешними источниками данных Container
может получать данные из API, локального хранилища или глобального
состояния. Для асинхронных операций используется useEffect
для подписки и синхронизации состояния.
Передача состояния и действий дочерним компонентам Container не занимается визуализацией напрямую. Он передает данные и методы управления через пропсы дочерним компонентам, которые отвечают исключительно за рендеринг.
import { html, component, useState, useEffect } from 'haunted';
import { TodoList } from './TodoList.js';
function TodoContainer() {
const [todos, setTodos] = useState([]);
useEffect(() => {
fetch('/api/todos')
.then(res => res.json())
.then(setTodos);
}, []);
const addTodo = (text) => {
const newTodo = { id: Date.now(), text, completed: false };
setTodos([...todos, newTodo]);
};
const toggleTodo = (id) => {
setTodos(todos.map(todo =>
todo.id === id ? { ...todo, completed: !todo.completed } : todo
));
};
return html`
<${TodoList}
.todos=${todos}
.on Add=${addTodo}
.onTog gle=${toggleTodo}
/>
`;
}
customElements.define('todo-container', component(TodoContainer));
Разбор примера:
useState используется для управления локальным
состоянием списка задач.useEffect обеспечивает загрузку данных при монтировании
компонента.addTodo и toggleTodo передаются в
дочерний компонент через пропсы.TodoList) отвечает только за
визуализацию и обработку событий интерфейса.Разделение ответственности Логика данных полностью отделена от UI. Это упрощает сопровождение и тестирование.
Повторное использование компонентов Компоненты, отвечающие за отображение, становятся универсальными и независимыми от источников данных.
Легкость тестирования Container можно протестировать отдельно, подставляя фиктивные данные и проверяя корректность методов управления состоянием.
Упрощение масштабирования В крупных приложениях добавление новых источников данных или изменение логики не требует вмешательства в UI-компоненты.
Для более сложных сценариев Container может использовать:
useEffect и отдельные методы.Пример с useReducer:
import { html, component, useReducer, useEffect } from 'haunted';
import { TodoList } from './TodoList.js';
const initialState = { todos: [], loading: true, error: null };
function reducer(state, action) {
switch (action.type) {
case 'SET_TODOS':
return { ...state, todos: action.payload, loading: false };
case 'ADD_TODO':
return { ...state, todos: [...state.todos, action.payload] };
case 'TOGGLE_TODO':
return {
...state,
todos: state.todos.map(todo =>
todo.id === action.payload ? { ...todo, completed: !todo.completed } : todo
)
};
case 'SET_ERROR':
return { ...state, error: action.payload, loading: false };
default:
return state;
}
}
function TodoContainer() {
const [state, dispatch] = useReducer(reducer, initialState);
useEffect(() => {
fetch('/api/todos')
.then(res => res.json())
.then(todos => dispatch({ type: 'SET_TODOS', payload: todos }))
.catch(err => dispatch({ type: 'SET_ERROR', payload: err }));
}, []);
const addTodo = (text) => {
const newTodo = { id: Date.now(), text, completed: false };
dispatch({ type: 'ADD_TODO', payload: newTodo });
};
const toggleTodo = (id) => dispatch({ type: 'TOGGLE_TODO', payload: id });
return html`
<${TodoList}
.todos=${state.todos}
.loading=${state.loading}
.error=${state.error}
.on Add=${addTodo}
.onTog gle=${toggleTodo}
/>
`;
}
customElements.define('todo-container', component(TodoContainer));
Container Pattern в Haunted позволяет строить чистые, масштабируемые и легко тестируемые приложения, где каждый компонент выполняет строго определенную роль — либо управление данными, либо визуализацию.