Рефы и доступ к методам

Работа с библиотекой Cleave.js почти всегда опирается на доступ к экземпляру маскировщика. Внутренний объект Cleave хранит состояние форматирования, правила обработки ввода и методы управления значением поля. Именно через него реализуется программное управление вводом, синхронизация с внешним состоянием и интеграция с фреймворками.

В большинстве случаев экземпляр создаётся через конструктор:

import Cleave from 'cleave.js';

const cleave = new Cleave('#input', {
  numeral: true,
  numeralThousandsGroupStyle: 'thousand'
});

Переменная cleave и есть реф на активный экземпляр. Через неё выполняется весь дальнейший контроль поведения поля.


Основная роль рефа экземпляра

Реф выполняет функцию связующего слоя между DOM-элементом и логикой форматирования. Без него невозможно:

  • программно изменять значение поля
  • получать «сырое» значение без форматирования
  • принудительно перерисовывать формат
  • корректно уничтожать инстанс при размонтировании UI
  • синхронизировать внешнее состояние приложения с вводом

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


Получение и хранение ссылки на экземпляр

Прямое сохранение ссылки

Самый простой вариант — хранение результата конструктора:

let cleaveInstance = null;

function init() {
  cleaveInstance = new Cleave('#phone', {
    phone: true,
    phoneRegionCode: 'US'
  });
}

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


Хранение в объекте состояния

В архитектурах с модульностью экземпляр часто помещается в состояние модуля:

const formController = {
  cleave: null,

  init() {
    this.cleave = new Cleave('#card', {
      creditCard: true
    });
  }
};

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


Работа с рефами в React

В React интеграции используется механизм useRef, поскольку прямой доступ к DOM и экземплярам должен быть стабилизирован между рендерами.

import { useEffect, useRef } from 'react';
import Cleave from 'cleave.js';

function CardInput() {
  const inputRef = useRef(null);
  const cleaveRef = useRef(null);

  useEffect(() => {
    cleaveRef.current = new Cleave(inputRef.current, {
      creditCard: true
    });

    return () => {
      cleaveRef.current?.destroy();
    };
  }, []);

  return <input ref={inputRef} />;
}

Ключевой момент: useRef используется не только для DOM, но и для хранения экземпляра Cleave, чтобы избежать пересоздания при рендерах.


Методы экземпляра Cleave.js

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

setRawValue

Устанавливает «сырое» значение без форматирования.

cleave.setRawValue('4111111111111111');

После вызова значение автоматически форматируется согласно конфигурации (например, как номер карты).


getRawValue

Возвращает значение без маски и форматирования.

const raw = cleave.getRawValue();

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


getFormattedValue

Возвращает текущее отображаемое значение с форматированием.

const formatted = cleave.getFormattedValue();

Полезно для логирования или отображения предпросмотра.


setPhoneRegion

Изменяет регион для телефонной маски на лету.

cleave.setPhoneRegion('RU');

При изменении региона Cleave пересчитывает формат ввода и обновляет отображение.


destroy

Полностью уничтожает экземпляр и возвращает поле к обычному input.

cleave.destroy();

Этот метод критически важен при работе в SPA, где компоненты часто монтируются и размонтируются.

После вызова:

  • удаляются все обработчики событий
  • сбрасываются внутренние состояния
  • DOM-элемент возвращается в исходный вид

Доступ к внутреннему input через реф

Иногда требуется прямой доступ к DOM-элементу, который управляется Cleave.

const input = cleave.element;
input.focus();

Свойство element хранит ссылку на исходный input. Через него можно выполнять любые DOM-операции, не связанные с форматированием.


Обновление значения через внешний state

При интеграции с UI-фреймворками часто возникает необходимость синхронизации внешнего состояния и Cleave-инстанса.

Пример синхронизации

function updateValue(value) {
  if (!cleaveInstance) return;

  cleaveInstance.setRawValue(value);
}

Важно использовать именно setRawValue, а не прямое изменение DOM, иначе форматирование может нарушиться.


Повторная инициализация через реф

В некоторых случаях требуется пересоздание экземпляра при изменении конфигурации.

function reinit(options) {
  if (cleaveInstance) {
    cleaveInstance.destroy();
  }

  cleaveInstance = new Cleave('#input', options);
}

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


Управление несколькими экземплярами

При наличии нескольких полей каждый экземпляр хранится отдельно:

const cleaves = {
  phone: null,
  card: null,
  date: null
};

cleaves.phone = new Cleave('#phone', { phone: true });
cleaves.card = new Cleave('#card', { creditCard: true });
cleaves.date = new Cleave('#date', { date: true });

Это позволяет централизованно управлять всеми масками формы.


Особенности жизненного цикла рефа

Экземпляр Cleave живёт независимо от DOM-рендера, поэтому важно контролировать его вручную:

  • создание — при инициализации компонента
  • обновление — через методы, а не через пересоздание DOM
  • уничтожение — строго при размонтировании

Игнорирование destroy() приводит к утечкам памяти и дублированию обработчиков событий.


Частые ошибки при работе с рефами

Потеря ссылки на экземпляр

new Cleave('#input', {...});

Без сохранения результата невозможно управлять экземпляром после создания.


Прямое изменение value

input.value = '1234';

Такой подход обходит Cleave и нарушает форматирование. Корректный вариант:

cleave.setRawValue('1234');

Повторная инициализация без destroy

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

cleave = new Cleave('#input', {...}); // без destroy — ошибка архитектуры

Архитектурная роль рефа в приложении

Реф Cleave-инстанса выступает как низкоуровневый контроллер ввода. Он находится ниже уровня бизнес-логики и выше DOM-слоя. Через него проходят все операции:

  • нормализация данных
  • форматирование отображения
  • синхронизация с внешним состоянием
  • очистка и сброс

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