Обработка пользовательского ввода

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

Основными элементами механизма ввода являются:

  • виртуальные DOM-события;
  • действия (actions);
  • состояние приложения (state);
  • функции-представления (view).

Каждое пользовательское действие — клик, ввод текста, изменение фокуса — обрабатывается через событие DOM, связанное с конкретным действием Hyperapp.


События и их привязка к действиям

В Hyperapp события DOM указываются непосредственно в JSX или h()-вызовах. Вместо передачи callback-функций используются действия, которые автоматически получают событие или полезную нагрузку.

Пример обработки клика:

const state = { count: 0 }

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

const view = (state, actions) =>
  h("button", { onclick: actions.increment }, state.count)

В этом примере:

  • onclick связан напрямую с actions.increment;
  • действие вызывается автоматически при событии;
  • новое состояние возвращается как чистый объект.

Передача данных из событий

Большинство событий ввода содержат полезные данные: текстовое значение, состояние чекбокса, выбранный элемент. Hyperapp передаёт объект события в действие, если оно ожидает аргумент.

const actions = {
  setValue: (state, event) => ({
    value: event.target.value
  })
}

Привязка в представлении:

h("input", {
  type: "text",
  value: state.value,
  oninput: actions.setValue
})

Здесь event.target.value извлекается напрямую внутри действия, без промежуточных обработчиков.


Управляемые элементы формы

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

Текстовое поле:

const state = { text: "" }

const actions = {
  updateText: (state, e) => ({ text: e.target.value })
}

const view = (state, actions) =>
  h("input", {
    value: state.text,
    oninput: actions.updateText
  })

Ключевые свойства:

  • value берётся из состояния;
  • изменение значения инициирует действие;
  • состояние — единственный источник истины.

Чекбоксы и переключатели

Для элементов с логическим состоянием используется checked:

const state = { enabled: false }

const actions = {
  toggle: (state, e) => ({ enabled: e.target.checked })
}

const view = (state, actions) =>
  h("input", {
    type: "checkbox",
    checked: state.enabled,
    onchange: actions.toggle
  })

Событие onchange предпочтительнее для чекбоксов, так как отражает окончательное состояние элемента.


Работа с select и option

Выпадающие списки обрабатываются аналогично текстовым полям:

const state = { color: "red" }

const actions = {
  selectColor: (state, e) => ({ color: e.target.value })
}

const view = (state, actions) =>
  h("select", { onchange: actions.selectColor, value: state.color }, [
    h("option", { value: "red" }, "Red"),
    h("option", { value: "green" }, "Green"),
    h("option", { value: "blue" }, "Blue")
  ])

Значение select полностью определяется состоянием, что исключает рассинхронизацию.


Передача аргументов в действия

Иногда требуется передать в действие не объект события, а конкретное значение. Для этого используется частичное применение:

const actions = {
  removeItem: (state, id) => ({
    items: state.items.filter(item => item.id !== id)
  })
}

Привязка:

h("button", {
  onclick: () => actions.removeItem(item.id)
}, "Удалить")

Такой подход отделяет данные от DOM-события и повышает читаемость логики.


Предотвращение стандартного поведения

Для событий вроде отправки формы требуется отмена стандартного действия браузера. Это делается внутри действия:

const actions = {
  submit: (state, e) => {
    e.preventDefault()
    return { submitted: true }
  }
}

Использование:

h("form", { onsubmit: actions.submit }, [...])

Hyperapp не ограничивает доступ к объекту события, позволяя использовать preventDefault, stopPropagation и другие методы.


Композиция действий для сложного ввода

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

const actions = {
  updateEmail: (state, e) => ({ email: e.target.value }),

  submitForm: state => [
    state,
    (actions) => {
      if (state.email.includes("@")) {
        actions.send()
      }
    }
  ],

  send: state => ({ sent: true })
}

Массив позволяет сначала обновить состояние, затем выполнить побочный эффект.


Валидация пользовательского ввода

Валидация реализуется как часть действий или вычисляемых данных представления.

Пример простой проверки:

const state = {
  password: "",
  error: null
}

const actions = {
  setPassword: (state, e) => {
    const value = e.target.value
    return {
      password: value,
      error: value.length < 6 ? "Слишком короткий пароль" : null
    }
  }
}

Отображение ошибки:

state.error && h("span", { class: "error" }, state.error)

Вся логика проверки сосредоточена в одном месте — действии.


Клавиатурные события

Hyperapp поддерживает стандартные клавиатурные события:

const actions = {
  handleKey: (state, e) => {
    if (e.key === "Enter") {
      return { submitted: true }
    }
  }
}

Использование:

h("input", { onkeydown: actions.handleKey })

Такой подход удобен для реализации горячих клавиш и управления с клавиатуры.


Обработка фокуса и blur

События onfocus и onblur используются для управления состоянием интерфейса:

const state = { focused: false }

const actions = {
  focus: () => ({ focused: true }),
  blur: () => ({ focused: false })
}

const view = (state, actions) =>
  h("input", {
    onfocus: actions.focus,
    onblur: actions.blur,
    class: state.focused ? "active" : ""
  })

Состояние фокуса становится частью общего состояния приложения.


Итоговая модель обработки ввода

Обработка пользовательского ввода в Hyperapp основана на следующих принципах:

  • все изменения проходят через действия;
  • DOM-события не содержат бизнес-логики;
  • состояние полностью управляет интерфейсом;
  • действия остаются чистыми и предсказуемыми;
  • побочные эффекты изолируются.

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