Cleave.js используется как слой маскирования и форматирования ввода,
но в React его применение требует учета модели управления состоянием
компонента и жизненного цикла DOM-узлов. В React нельзя напрямую
полагаться на императивное изменение DOM без синхронизации с состоянием,
поэтому интеграция строится вокруг контролируемых компонентов и ссылок
(ref).
Базовый сценарий установки:
npm install cleave.js
или
yarn add cleave.js
Наиболее прямой способ интеграции — использование useRef
и инициализация Cleave после монтирования элемента.
import React, { useEffect, useRef } from "react";
import Cleave from "cleave.js";
export default function PhoneInput() {
const inputRef = useRef(null);
const cleaveInstance = useRef(null);
useEffect(() => {
if (inputRef.current) {
cleaveInstance.current = new Cleave(inputRef.current, {
phone: true,
phoneRegionCode: "KZ"
});
}
return () => {
if (cleaveInstance.current) {
cleaveInstance.current.destroy();
cleaveInstance.current = null;
}
};
}, []);
return <input ref={inputRef} type="text" />;
}
Ключевым моментом является обязательное уничтожение экземпляра при
размонтировании компонента через destroy(), иначе возможны
утечки памяти и неконсистентное поведение DOM.
Cleave.js по своей природе модифицирует значение input в DOM, что вступает в конфликт с полностью контролируемыми компонентами React.
В этом режиме React не управляет значением напрямую:
<input ref={inputRef} type="text" />
Cleave берет управление вводом на себя, изменяя отображаемое значение.
При использовании value и onChange
возникает проблема двойного источника истины:
const [value, setValue] = useState("");
<input
value={value}
onCha nge={(e) => setValue(e.target.value)}
/>
Cleave при этом может конфликтовать с обновлением React, вызывая дергание значения или потерю форматирования. Поэтому требуется синхронизация через события Cleave.
Экземпляр Cleave предоставляет событие onValueChanged,
которое позволяет синхронизировать состояние React с уже
отформатированным значением.
import React, { useEffect, useRef, useState } from "react";
import Cleave from "cleave.js";
export default function CardInput() {
const inputRef = useRef(null);
const cleaveRef = useRef(null);
const [value, setValue] = useState("");
useEffect(() => {
if (inputRef.current) {
cleaveRef.current = new Cleave(inputRef.current, {
creditCard: true,
onValueChanged: function (e) {
setValue(e.target.value);
}
});
}
return () => {
cleaveRef.current?.destroy();
};
}, []);
return <input ref={inputRef} value={value} type="text" />;
}
Такой подход делает React источником состояния, а Cleave — источником форматирования.
В React часто требуется устанавливать значение извне (например, при загрузке данных). Cleave хранит «сырое» и «форматированное» значения отдельно, поэтому необходимо использовать API экземпляра.
cleaveRef.current.setRawValue("4111111111111111");
или
cleaveRef.current.setValue("4111 1111 1111 1111");
Различие между методами критично: setRawValue работает с
исходными данными, setValue — с форматированными.
Экземпляр предоставляет два основных метода:
const raw = cleaveRef.current.getRawValue();
const formatted = cleaveRef.current.getFormattedValue();
getRawValue() используется для отправки данных на
сервер, так как возвращает очищенную строку без маски.
React-компоненты часто требуют изменения конфигурации Cleave при изменении props. Например, смена типа ввода:
useEffect(() => {
if (cleaveRef.current) {
cleaveRef.current.destroy();
cleaveRef.current = new Cleave(inputRef.current, {
numeral: props.isNumber,
date: props.isDate,
datePattern: ["d", "m", "Y"]
});
}
}, [props.type]);
Такой подход предполагает пересоздание экземпляра при изменении критичных настроек.
В React-формах Cleave используется как слой UI-форматирования, а не как источник бизнес-данных. Валидация и отправка происходят через состояние React:
function handleSubmit() {
const rawValue = cleaveRef.current.getRawValue();
fetch("/api/submit", {
method: "POST",
body: JSON.stringify({ card: rawValue })
});
}
Форматированное значение не должно использоваться в логике сервера.
Cleave поддерживает числовые и датовые маски, что особенно полезно в формах с финансовыми данными.
new Cleave(inputRef.current, {
numeral: true,
numeralThousandsGroupStyle: "thousand"
});
new Cleave(inputRef.current, {
date: true,
datePattern: ["d", "m", "Y"]
});
В React важно учитывать локализацию и возможные различия форматов, так как Cleave не выполняет автоматическую интернационализацию.
При использовании нескольких зависимых полей (например, сумма + комиссия) Cleave может требовать внешнего контроля перерендера:
useEffect(() => {
if (cleaveRef.current) {
cleaveRef.current.setRawValue(calculatedValue);
}
}, [calculatedValue]);
Важно избегать бесконечных циклов обновления состояния: изменение Cleave → setState → rerender → повторная установка значения.
React StrictMode в development-режиме может вызывать двойное монтирование компонентов, поэтому корректное уничтожение Cleave критично:
return () => {
if (cleaveRef.current) {
cleaveRef.current.destroy();
cleaveRef.current = null;
}
};
Игнорирование этого шага приводит к дублированию обработчиков событий и некорректной работе input.
Распространённые проблемы при использовании Cleave в React:
value и Cleave без
синхронизации;destroy();formatted value вместо
raw value для бизнес-логики;ref, что приводит к невозможности доступа к
DOM-элементу;onValueChanged без
оптимизации, вызывающее лишние ререндеры.Наиболее устойчивый подход в React-экосистеме заключается в разделении ролей:
onValueChanged;Такая модель снижает связанность и предотвращает конфликт двух систем управления DOM.