Hyperapp — это минималистичный фронтенд-фреймворк, который строится вокруг идеи единого источника правды — состояния приложения. Состояние (state) в Hyperapp представляет собой простой JavaScript-объект, который описывает всю текущую информацию интерфейса. Любые изменения в интерфейсе происходят исключительно через обновление этого состояния, что обеспечивает предсказуемость и прозрачность работы приложения.
Состояние объявляется при создании приложения через объект
state. Пример:
import { h, app } from "hyperapp";
const state = {
count: 0
};
const view = (state, actions) => (
<div>
<h1>{state.count}</h1>
<button oncl ick={actions.increment}>+</button>
<button oncl ick={actions.decrement}>-</button>
</div>
);
const actions = {
increment: (value) => (state) => ({ count: state.count + 1 }),
decrement: (value) => (state) => ({ count: state.count - 1 })
};
app({ state, actions, view, root: document.body });
Ключевые моменты:
state — объект с данными приложения.actions.Hyperapp строго следует принципу иммутабельности: состояние не изменяется напрямую, а создается новый объект с обновленными значениями. Это позволяет:
Пример обновления состояния с иммутабельностью:
const actions = {
toggle: () => (state) => ({ ...state, active: !state.active })
};
Оператор ...state создает новый объект с копией всех
полей, а active: !state.active обновляет конкретное
свойство.
Actions в Hyperapp — это функции, которые принимают аргументы и возвращают либо новый объект состояния, либо функцию, которая модифицирует состояние. Существуют два способа определения:
const actions = {
increment: () => ({ count: state.count + 1 })
};
const actions = {
increment: () => (state) => ({ count: state.count + 1 })
};
Преимущество второго подхода — доступ к текущему состоянию и возможность производить вычисления на основе него.
Hyperapp позволяет управлять вложенными объектами и массивами. Для обновления вложенного состояния используют глубокое копирование или функции-помощники:
const state = {
user: { name: "Alice", age: 25 },
todos: [{ text: "Learn Hyperapp", done: false }]
};
const actions = {
setUserName: (name) => (state) => ({
user: { ...state.user, name }
}),
toggleTodo: (index) => (state) => ({
todos: state.todos.map((todo, i) =>
i === index ? { ...todo, done: !todo.done } : todo
)
})
};
Особенности:
map,
filter или concat, чтобы не мутировать
исходный массив.Hyperapp позволяет связывать изменение состояния с побочными эффектами через эффекты. Эффект — это функция, которая может выполнять асинхронные операции, сетевые запросы или работу с локальным хранилищем. Пример:
const actions = {
fetchData: () => (state, actions) => [
state,
(dispatch) => fetch("/api/data")
.then(res => res.json())
.then(data => dispatch(actions.setData, data))
],
setData: (data) => (state) => ({ data })
};
Важно: эффекты не изменяют состояние напрямую. Они возвращают новые данные через действия, что сохраняет предсказуемость приложения.
Hyperapp не разделяет состояние на глобальное и локальное, все данные
хранятся в едином объекте state. Для изоляции состояния
компонентов используют вложенные структуры и actions, привязанные только
к нужной части состояния:
const state = {
counter: { value: 0 },
userForm: { name: "", email: "" }
};
const actions = {
counter: {
increment: () => (state) => ({
counter: { ...state.counter, value: state.counter.value + 1 }
})
},
userForm: {
setName: (name) => (state) => ({
userForm: { ...state.userForm, name }
})
}
};
Такой подход позволяет сохранять ясную структуру состояния и предотвращает нежелательные изменения других частей приложения.
Этот подход делает Hyperapp мощным инструментом для создания приложений с понятным, управляемым и легко отлаживаемым состоянием.