Hyperapp — это минималистичный JavaScript-фреймворк, ориентированный на декларативное управление состоянием и построение пользовательских интерфейсов с использованием виртуального DOM. Работа с формами в Hyperapp требует особого подхода, особенно если форма содержит множество полей, сложные вложенные структуры или динамические элементы.
В Hyperapp все состояния приложения хранятся в одном объекте
state. Каждое поле формы должно быть связано с
соответствующим свойством состояния. Изменение значения поля выполняется
через actions, которые модифицируют состояние, вызывая
ререндер компонента.
const state = {
form: {
name: '',
email: '',
password: ''
}
};
const actions = {
updateField: (field, value) => state => ({
form: { ...state.form, [field]: value }
})
};
Пример выше демонстрирует универсальный action для обновления любого поля формы, что удобно для больших форм с множеством полей.
Для форм с вложенными объектами или массивами требуется использовать глубокое обновление состояния. Например, форма заказа с товарами:
const state = {
order: {
customer: { name: '', phone: '' },
items: [{ product: '', quantity: 1 }]
}
};
const actions = {
updateCustomerField: (field, value) => state => ({
order: { ...state.order, customer: { ...state.order.customer, [field]: value } }
}),
updateItemField: (index, field, value) => state => ({
order: {
...state.order,
items: state.order.items.map((item, i) =>
i === index ? { ...item, [field]: value } : item
)
}
}),
addItem: () => state => ({
order: { ...state.order, items: [...state.order.items, { product: '', quantity: 1 }] }
}),
removeItem: index => state => ({
order: { ...state.order, items: state.order.items.filter((_, i) => i !== index) }
})
};
Данный подход обеспечивает иммутабельность состояния и упрощает управление динамическими элементами формы.
Hyperapp использует JSX-подобный синтаксис для описания интерфейса.
Значения полей формы привязываются к состоянию через value
и обновляются с помощью событий oninput или
onchange.
const view = (state, actions) => (
<form>
<input
type="text"
value={state.order.customer.name}
onin put={e => actions.updateCustomerField('name', e.target.value)}
/>
<input
type="text"
value={state.order.customer.phone}
onin put={e => actions.updateCustomerField('phone', e.target.value)}
/>
{state.order.items.map((item, index) => (
<div>
<input
type="text"
value={item.product}
onin put={e => actions.updateItemField(index, 'product', e.target.value)}
/>
<input
type="number"
value={item.quantity}
onin put={e => actions.updateItemField(index, 'quantity', +e.target.value)}
/>
<button type="button" oncl ick={() => actions.removeItem(index)}>Удалить</button>
</div>
))}
<button type="button" oncl ick={actions.addItem}>Добавить товар</button>
</form>
);
Важным моментом является привязка к value, а не
defaultValue, чтобы обеспечить синхронизацию состояния с
интерфейсом.
Сложные формы часто требуют проверки данных. В Hyperapp для этого
обычно создается отдельный объект состояния errors, который
хранит сообщения об ошибках для каждого поля.
const state = {
form: { name: '', email: '' },
errors: {}
};
const actions = {
validateField: (field, value) => state => {
const errors = { ...state.errors };
if (field === 'email' && !value.includes('@')) {
errors[field] = 'Некорректный email';
} else {
delete errors[field];
}
return { errors };
}
};
Каждое поле привязывается к соответствующей проверке:
<input
type="email"
value={state.form.email}
onin put={e => {
actions.updateField('email', e.target.value);
actions.validateField('email', e.target.value);
}}
/>
{state.errors.email && <span>{state.errors.email}</span>}
Для форм, где количество полей может меняться во время работы приложения, используется подход с массивами состояния и методами добавления/удаления элементов. Это особенно полезно для форм с заказами, тегами, опциями и другими повторяющимися структурами.
const actions = {
addTag: () => state => ({ tags: [...state.tags, ''] }),
updateTag: (index, value) => state => ({
tags: state.tags.map((tag, i) => i === index ? value : tag)
}),
removeTag: index => state => ({
tags: state.tags.filter((_, i) => i !== index)
})
};
Hyperapp поддерживает асинхронные actions через функции, возвращающие
Promise. Для отправки данных формы на сервер можно
использовать fetch:
const actions = {
submitForm: () => async (state, actions) => {
try {
const response = await fetch('/api/submit', {
method: 'POST',
body: JSON.stringify(state.form),
headers: { 'Content-Type': 'application/json' }
});
const data = await response.json();
actions.formSubmitted(data);
} catch (error) {
actions.formError(error.message);
}
},
formSubmitted: data => state => ({ submissionResult: data }),
formError: message => state => ({ submissionError: message })
};
Использование асинхронных actions позволяет сохранять поток управления и логику формы в рамках Hyperapp, не прибегая к внешним библиотекам управления состоянием.
Для масштабных форм рекомендуется разбивать их на модули состояния и actions. Например, каждый раздел формы (личные данные, адреса, заказы) может иметь собственное под-дерево состояния и набор actions, объединяемый в главный объект. Такой подход упрощает поддержку, тестирование и переиспользование компонентов.
Систематическое применение этих принципов обеспечивает надежное управление сложными формами в Hyperapp, поддерживая чистоту кода, иммутабельность состояния и простоту масштабирования.