Действия (Actions) и обработка событий

Hyperapp использует минималистичный подход к управлению состоянием и обновлению интерфейса. Одной из ключевых концепций является система Actions, которая позволяет изменять состояние приложения реактивно и декларативно. Actions в Hyperapp представляют собой функции, которые принимают текущее состояние и возвращают новое состояние или функцию для асинхронных операций.

Основы Actions

Action — это чистая функция, которая получает два аргумента:

  1. state — текущее состояние приложения.
  2. actions — объект всех зарегистрированных actions, что позволяет вызывать другие actions внутри одной функции.

Простейший пример action:

const state = { count: 0 }

const actions = {
  increment: (state) => ({ count: state.count + 1 }),
  decrement: (state) => ({ count: state.count - 1 })
}

В этом примере increment и decrement возвращают новое состояние на основе предыдущего. Hyperapp автоматически объединяет результат с текущим состоянием, создавая новый объект состояния.

Actions и параметры

Actions могут принимать аргументы, что позволяет динамически изменять состояние:

const actions = {
  add: (amount) => (state) => ({ count: state.count + amount })
}

Вызов actions.add(5) увеличит count на 5. Такой подход делает код гибким и легко масштабируемым.

Асинхронные действия

Hyperapp поддерживает асинхронные операции через effect functions. Action может возвращать функцию вместо объекта состояния. Эта функция получает dispatch и state, что позволяет выполнять асинхронные вызовы и вызывать другие actions:

const actions = {
  fetchData: () => (state, actions) => {
    fetch('/api/data')
      .then(res => res.json())
      .then(data => actions.setData(data))
  },
  setData: (data) => ({ data })
}

Такой подход сохраняет реактивность и чистоту архитектуры, отделяя побочные эффекты от чистых изменений состояния.

Обработка событий

Hyperapp связывает события с actions напрямую через атрибуты элементов. События могут быть стандартными DOM-событиями: onclick, oninput, onsubmit и другими.

const view = (state, actions) => (
  <div>
    <button oncl ick={actions.increment}>Увеличить</button>
    <button oncl ick={actions.decrement}>Уменьшить</button>
    <p>Счётчик: {state.count}</p>
  </div>
)

События могут передавать аргументы через стрелочные функции:

<button oncl ick={() => actions.add(10)}>Добавить 10</button>

Композиция Actions

Actions могут быть сложными и составными. Один action может вызывать другой для создания цепочек логики:

const actions = {
  increment: (state) => ({ count: state.count + 1 }),
  incrementIfEven: (state, actions) => {
    if (state.count % 2 === 0) actions.increment()
  }
}

Такая композиция упрощает управление состоянием без необходимости дублировать код.

Обработка форм и вводимых данных

Для управления формами и input-элементами используется привязка событий к actions:

const state = { text: '' }

const actions = {
  updateText: (value) => ({ text: value })
}

const view = (state, actions) => (
  <input type="text" value={state.text} onin put={(e) => actions.updateText(e.target.value)} />
)

При каждом изменении текста вызывается updateText, обновляя состояние и перерисовывая интерфейс.

События с побочными эффектами

Для интеграции с внешними системами или выполнения асинхронных операций используют комбинацию actions и effect functions:

const actions = {
  logClick: () => (state, actions) => {
    console.log('Клик зафиксирован', state)
  }
}

<button oncl ick={actions.logClick}>Логировать</button>

Это позволяет централизованно управлять логикой и сохранять реактивность приложения.

Ключевые принципы работы с Actions

  1. Чистые функции для состояния — каждое действие должно быть предсказуемым и не изменять состояние напрямую.
  2. Возврат нового состояния — Hyperapp автоматически объединяет результаты с текущим состоянием.
  3. Разделение чистой логики и побочных эффектов — асинхронные операции и взаимодействие с внешним миром выполняются через effect functions.
  4. Прямое связывание с событиями — события DOM напрямую вызывают actions, обеспечивая простую и декларативную архитектуру.
  5. Композиция и переиспользование — Actions могут вызывать друг друга, образуя гибкие цепочки логики.

Практика применения

Частой задачей является создание динамических списков с действиями добавления, удаления и обновления элементов:

const state = { items: [] }

const actions = {
  addItem: (text) => (state) => ({ items: [...state.items, text] }),
  removeItem: (index) => (state) => ({
    items: state.items.filter((_, i) => i !== index)
  })
}

const view = (state, actions) => (
  <div>
    <input onin put={(e) => actions.addItem(e.target.value)} placeholder="Добавить элемент" />
    <ul>
      {state.items.map((item, i) => (
        <li>
          {item} <button oncl ick={() => actions.removeItem(i)}>Удалить</button>
        </li>
      ))}
    </ul>
  </div>
)

Этот пример демонстрирует, как Actions позволяют управлять состоянием динамически, обеспечивая прямую реакцию на пользовательские события и изменения интерфейса без лишней сложности.

Hyperapp делает Actions центральной частью архитектуры, обеспечивая компактный, чистый и предсказуемый способ управления состоянием и обработки событий в приложении.