Controlled vs Uncontrolled

В SkateJS различие между controlled и uncontrolled компонентами является ключевым при управлении состоянием элементов пользовательского интерфейса. Оно напрямую влияет на то, как данные передаются и обновляются, а также на реактивность компонентов.


Uncontrolled компоненты

Uncontrolled компоненты — это элементы, которые самостоятельно управляют своим внутренним состоянием без прямого вмешательства извне. В SkateJS это реализуется через локальные свойства и внутренние механизмы обновления.

Пример:

import { define, Component, h, prop } from 'skatejs';

class UncontrolledInput extends Component {
  static props = {
    value: prop.string()
  };

  constructor() {
    super();
    this.value = '';
  }

  renderCallback() {
    return h('input', {
      value: this.value,
      onInput: (event) => {
        this.value = event.target.value;
      }
    });
  }
}

define('uncontrolled-input', UncontrolledInput);

Особенности:

  • Состояние хранится внутри компонента. Свойство value обновляется локально при событии input.
  • Родительский компонент не управляет значением напрямую. Он может только читать текущее состояние через DOM или специальные методы.
  • Простота реализации — нет необходимости синхронизировать состояние между компонентами.

Недостатки:

  • Сложно интегрировать с глобальным состоянием приложения.
  • При необходимости внешнего контроля нужно использовать дополнительные события и колбэки.

Controlled компоненты

Controlled компоненты полностью управляются извне. Значение любого свойства задаётся через пропсы, а любые изменения состояния передаются родителю для обработки.

Пример:

import { define, Component, h, prop } from 'skatejs';

class ControlledInput extends Component {
  static props = {
    value: prop.string(),
    onChange: prop.func()
  };

  renderCallback() {
    return h('input', {
      value: this.value || '',
      onInput: (event) => {
        this.onChange && this.onChange(event.target.value);
      }
    });
  }
}

define('controlled-input', ControlledInput);

Особенности:

  • Состояние полностью внешнее. Значение value передаётся через пропсы.
  • Обновление через колбэк. Любое изменение вызывает функцию onChange, которая позволяет родителю решать, нужно ли обновлять значение.
  • Прозрачность и предсказуемость. Любое состояние приложения может быть синхронизировано с контролируемым компонентом.

Недостатки:

  • Требует большего объёма кода, так как управление состоянием полностью лежит на родителе.
  • Сложнее в простых случаях, когда управление компонентом локально более удобно.

Основные различия

Характеристика Uncontrolled Controlled
Место хранения состояния Внутри компонента В родительском компоненте
Способ обновления Локальные события (onInput, onChange) Колбэки для передачи изменений наружу
Сложность Ниже Выше
Интеграция с глобальным состоянием Ограничена Полная
Предсказуемость поведения Частично Полная

Практические рекомендации

  • Использовать uncontrolled компоненты для простых интерактивных элементов, где нет необходимости синхронизировать данные с другими частями приложения.
  • Использовать controlled компоненты при работе с формами, состояниями, которые должны быть единообразно отражены в разных местах приложения.
  • Возможна комбинация подходов: компонент может быть uncontrolled по умолчанию, но принимать проп value для внешнего контроля при необходимости.

Внутренние механизмы SkateJS для управления состоянием

  • prop — основной способ определения реактивных свойств, которые автоматически вызывают ререндер при изменении.
  • renderCallback — функция, вызываемая при изменении любого реактивного свойства.
  • События DOM — используются для передачи изменений от uncontrolled компонентов наружу или для колбэков в controlled компонентах.

Пример комбинированного подхода

class MixedInput extends Component {
  static props = {
    value: prop.string(),
    defaultValue: prop.string(),
    onChange: prop.func()
  };

  constructor() {
    super();
    this.value = this.value || this.defaultValue || '';
  }

  renderCallback() {
    return h('input', {
      value: this.value,
      onInput: (event) => {
        const newValue = event.target.value;
        if (this.onChange) {
          this.onChange(newValue); // controlled
        } else {
          this.value = newValue; // uncontrolled fallback
        }
      }
    });
  }
}

define('mixed-input', MixedInput);

Такой подход позволяет компоненту работать как uncontrolled, если родитель не передаёт value, и как controlled, если передаётся.


Controlled и uncontrolled — фундаментальное различие в архитектуре SkateJS-компонентов, которое напрямую влияет на реактивность, интеграцию с формами и управляемость приложения. Понимание этих концепций позволяет создавать гибкие и предсказуемые интерфейсы.