Кросс-шаговые Behaviors
Координирование behaviors между несколькими шагами формы.
Обзор
Некоторые behaviors нуждаются в данных из нескольких шагов. Эти кросс-шаговые behaviors обрабатывают:
- Соотношение платёж/доход - Использует Шаг 1 (платёж) и Шаг 4/5 (доход)
- Умная ревалидация - Запускает валидацию при изменении зависимостей
- Контроль доступа по возрасту - Использует Шаг 2 (возраст) для контроля Шага 1 (поля кредита)
- Отслеживание аналитики - Мониторит поведение пользователя по всей форме
Почему разделять кросс-шаговые behaviors?
Преимущества разделения:
- Ясность - Легко видеть какие behaviors охватывают несколько шагов
- Поддерживаемость - Изменения в behaviors шагов не влияют на кросс-шаговую логику
- Документация - Кросс-шаговые зависимости явные
Реализация
import { computeFrom, disableWhen, revalidateWhen, watchField } from '@reformer/core/behaviors';
import type { BehaviorSchemaFn } from '@reformer/core/behaviors';
import type { FieldPath } from '@reformer/core';
import type { CreditApplicationForm } from '@/types';
export const crossStepBehaviorsSchema: BehaviorSchemaFn<CreditApplicationForm> = (
path: FieldPath<CreditApplicationForm>
) => {
// ==========================================
// 1. Соотношение платёж/доход
// Шаг 1: monthlyPayment
// Шаг 4: totalIncome
// Шаг 5: coBorrowersIncome
// ==========================================
computeFrom(
[path.monthlyPayment, path.totalIncome, path.coBorrowersIncome],
path.paymentToIncomeRatio,
(values) => {
const payment = values.monthlyPayment as number;
const mainIncome = values.totalIncome as number;
const coIncome = values.coBorrowersIncome as number;
const totalHouseholdIncome = (mainIncome || 0) + (coIncome || 0);
if (!totalHouseholdIncome || !payment) return 0;
return Math.round((payment / totalHouseholdIncome) * 100);
}
);
// Отключить paymentToIncomeRatio (только для чтения)
disableWhen(path.paymentToIncomeRatio, () => true);
// ==========================================
// 2. Ревалидировать платёж при изменении дохода
// Валидация проверяет платёж <= 50% дохода
// ==========================================
revalidateWhen(path.monthlyPayment, [path.totalIncome, path.coBorrowersIncome]);
// ==========================================
// 3. Контроль доступа по возрасту
// Шаг 2: age
// Шаг 1: поля кредита
// ==========================================
disableWhen(path.loanAmount, (form) => (form.age as number) < 18);
disableWhen(path.loanTerm, (form) => (form.age as number) < 18);
disableWhen(path.loanPurpose, (form) => (form.age as number) < 18);
// ==========================================
// 4. Отслеживание аналитики
// ==========================================
watchField(path.loanAmount, (value) => {
console.log('Сумма кредита изменена:', value);
// window.analytics?.track('loan_amount_changed', { amount: value });
});
watchField(path.interestRate, (value) => {
console.log('Процентная ставка рассчитана:', value);
// window.analytics?.track('interest_rate_computed', { rate: value });
});
watchField(path.employmentStatus, (value) => {
console.log('Статус занятости изменён:', value);
// window.analytics?.track('employment_status_changed', { status: value });
});
};
Разбор каждого behavior
1. Соотношение платёж/доход
Это критическая метрика для одобрения кредита:
- Входные данные: Ежемесячный платёж, доход заявителя, доход созаёмщиков
- Выходные данные: Процент (например, 35% означает что платёж составляет 35% дохода)
- Использование: Банки обычно требуют соотношение < 50%
Цепочка зависимостей:
loanAmount, loanTerm, interestRate
↓
monthlyPayment (Шаг 1)
↓
paymentToIncomeRatio ← totalIncome (Шаг 4)
← coBorrowersIncome (Шаг 5)
2. Умная ревалидация
Когда доход изменяется, нам нужно ревалидировать платёж:
// Правило валидации (реализуется в разделе Валидация)
validate(path.monthlyPayment, (payment, ctx) => {
const totalIncome = ctx.form.totalIncome.value.value || 0;
const coBorrowersIncome = ctx.form.coBorrowersIncome.value.value || 0;
const total = totalIncome + coBorrowersIncome;
if (payment > total * 0.5) {
return { code: 'maxPaymentToIncome', message: 'Платёж превышает 50% дохода' };
}
return null;
});
// Behavior: Запустить ревалидацию когда доход изменяется
revalidateWhen(path.monthlyPayment, [path.totalIncome, path.coBorrowersIncome]);
Зачем нужно:
- Пользователь заполняет информацию о кредите первым (Шаг 1)
- Затем заполняет доход (Шаг 4)
- Валидация платежа должна запуститься снова с новыми данными дохода
- Без
revalidateWhen, валидация запускается только когда платёж изменяется
3. Контроль доступа по возрасту
Предотвращает подачу заявки несовершеннолетними:
disableWhen(path.loanAmount, (form) => (form.age as number) < 18);
Поток:
- Пользователь вводит дату рождения (Шаг 2)
- Возраст вычисляется автоматически
- Если возраст < 18, поля кредита на Шаге 1 становятся отключены
- Пользователь не может продолжить с приложением
Это демонстрирует обратные зависимости - данные Шага 2 влияют на UI Шага 1.
4. Отслеживание аналитики
Мониторьте поведение пользователя для получения информации:
watchField(path.loanAmount, (value) => {
// Отслеживаем изменения суммы кредита
window.analytics?.track('loan_amount_changed', { amount: value });
});
Сценарии использования:
- Отслеживайте какие типы кредитов самые популярные
- Мониторьте распределение процентных ставок
- Анализируйте точки отказа в форме
- A/B тестирование разных потоков формы
В продакшене, интегрируйте со своей платформой аналитики:
import { analytics } from '@/services/analytics';
watchField(path.loanAmount, (value) => {
analytics.track('LoanAmountChanged', {
amount: value,
timestamp: Date.now(),
sessionId: getSessionId(),
});
});
Полный код
import { computeFrom, disableWhen, revalidateWhen, watchField } from '@reformer/core/behaviors';
import type { BehaviorSchemaFn } from '@reformer/core/behaviors';
import type { FieldPath } from '@reformer/core';
import type { CreditApplicationForm } from '@/types';
export const crossStepBehaviorsSchema: BehaviorSchemaFn<CreditApplicationForm> = (
path: FieldPath<CreditApplicationForm>
) => {
// Соотношение платёж/доход
computeFrom(
[path.monthlyPayment, path.totalIncome, path.coBorrowersIncome],
path.paymentToIncomeRatio,
(values) => {
const payment = values.monthlyPayment as number;
const mainIncome = values.totalIncome as number;
const coIncome = values.coBorrowersIncome as number;
const totalHouseholdIncome = (mainIncome || 0) + (coIncome || 0);
if (!totalHouseholdIncome || !payment) return 0;
return Math.round((payment / totalHouseholdIncome) * 100);
}
);
disableWhen(path.paymentToIncomeRatio, () => true);
// Умная ревалидация
revalidateWhen(path.monthlyPayment, [path.totalIncome, path.coBorrowersIncome]);
// Контроль доступа по возрасту
disableWhen(path.loanAmount, (form) => (form.age as number) < 18);
disableWhen(path.loanTerm, (form) => (form.age as number) < 18);
disableWhen(path.loanPurpose, (form) => (form.age as number) < 18);
// Отслеживание аналитики
watchField(path.loanAmount, (value) => {
console.log('Сумма кредита изменена:', value);
});
watchField(path.interestRate, (value) => {
console.log('Процентная ставка рассчитана:', value);
});
watchField(path.employmentStatus, (value) => {
console.log('Статус занятости изменён:', value);
});
};
Отображение кросс-шаговых данных
Показываем соотношение платёж/доход в виджете резюме:
import { useFormControl } from '@reformer/core';
function LoanSummary({ control }: Props) {
const { value: monthlyPayment } = useFormControl(control.monthlyPayment);
const { value: paymentToIncomeRatio } = useFormControl(control.paymentToIncomeRatio);
const isAcceptable = paymentToIncomeRatio <= 50;
return (
<div className="p-4 bg-gray-50 rounded">
<h3 className="font-semibold mb-2">Резюме кредита</h3>
<div className="flex justify-between mb-2">
<span>Ежемесячный платёж:</span>
<span className="font-bold">{monthlyPayment.toLocaleString()} ₽</span>
</div>
<div className="flex justify-between">
<span>Платёж к доходу:</span>
<span className={`font-bold ${isAcceptable ? 'text-green-600' : 'text-red-600'}`}>
{paymentToIncomeRatio}%
</span>
</div>
{!isAcceptable && (
<p className="text-sm text-red-600 mt-2">
Платёж превышает 50% семейного дохода. Рассмотрите: - Сокращение суммы кредита - Продление
срока кредита - Добавление созаёмщиков
</p>
)}
</div>
);
}
Результат
Кросс-шаговые behaviors теперь предоставляют:
- ✅ Расчет соотношения платёж/доход
- ✅ Умная ревалидация при изменении дохода
- ✅ Контроль доступа по возрасту (предотвращает подачу заявок несовершеннолетними)
- ✅ Отслеживание аналитики для получения информации
Ключевые выводы
- Разделяйте кросс-шаговые behaviors для ясности
revalidateWhenгарантирует что валидация остаётся актуальной- Обратные зависимости возможны (Шаг 2 → Шаг 1)
- Аналитика через
watchFieldдля мониторинга - Отображайте кросс-шаговые данные в резюме/виджетах
Следующий шаг
Теперь давайте объединим все behaviors и зарегистрируем их с формой в финальном разделе.