Combining & Registering Behaviors
Assembling all behaviors and integrating with the form.
Overviewβ
We've created behaviors for each step plus cross-step behaviors. Now let's:
- Create the main behavior file that combines everything
- Register behaviors with the form
- Test that all behaviors work together
- Review the complete file structure
Creating the Main Behavior Fileβ
Create the main behavior file that imports and applies all step behaviors:
touch reformer-tutorial/src/forms/credit-application/schemas/behaviors/credit-application.behaviors.ts
Implementationβ
import type { BehaviorSchemaFn, FieldPath } from '@reformer/core';
import type { CreditApplicationForm } from '@/types';
// Import step behaviors
import { loanBehaviorSchema } from './steps/step-1-loan-info.behaviors';
import { personalBehaviorSchema } from './steps/step-2-personal-info.behaviors';
import { contactBehaviorSchema } from './steps/step-3-contact-info.behaviors';
import { employmentBehaviorSchema } from './steps/step-4-employment.behaviors';
import { additionalBehaviorSchema } from './steps/step-5-additional-info.behaviors';
import { crossStepBehaviorsSchema } from './cross-step.behaviors';
/**
* Complete behavior schema for Credit Application Form
*
* Organized by form steps for maintainability:
* - Step 1: Loan Information
* - Step 2: Personal Information
* - Step 3: Contact Information
* - Step 4: Employment
* - Step 5: Additional Information
* - Cross-Step: Behaviors spanning multiple steps
*/
export const creditApplicationBehaviors: BehaviorSchemaFn<CreditApplicationForm> = (
path: FieldPath<CreditApplicationForm>
) => {
// ==========================================
// Step 1: Loan Information
// ==========================================
loanBehaviorSchema(path);
// ==========================================
// Step 2: Personal Information
// ==========================================
personalBehaviorSchema(path);
// ==========================================
// Step 3: Contact Information
// ==========================================
contactBehaviorSchema(path);
// ==========================================
// Step 4: Employment
// ==========================================
employmentBehaviorSchema(path);
// ==========================================
// Step 5: Additional Information
// ==========================================
additionalBehaviorSchema(path);
// ==========================================
// Cross-Step Behaviors
// ==========================================
crossStepBehaviorsSchema(path);
};
Registering with the Formβ
Update your form creation function to include behaviors:
import { createForm } from '@reformer/core';
import { creditApplicationSchema } from './credit-application.schema';
import { creditApplicationBehaviors } from '../behaviors/credit-application.behaviors';
import type { CreditApplicationForm } from '@/types';
export function createCreditApplicationForm() {
return createForm<CreditApplicationForm>({
schema: creditApplicationSchema,
behaviors: creditApplicationBehaviors, // β Register behaviors here
// validation will be added in the next section
});
}
That's it! Behaviors are now active when the form is created.
Testing All Behaviorsβ
Create a comprehensive test checklist:
Step 1: Loan Informationβ
- Interest rate updates when loan type changes
- Interest rate gets discount for major cities
- Interest rate gets discount for property owners
- Monthly payment calculates automatically
- Mortgage fields show only for mortgage
- Car fields show only for car loans
- Fields clear when switching loan types
Step 2: Personal Informationβ
- Full name generates from first, last, middle names
- Age calculates from birth date
- Both computed fields are disabled
Step 3: Contact Informationβ
- Residence address hides when "same as registration" checked
- Registration address copies to residence address
- Residence address disables when same as registration
- Manual changes to residence address work when unchecked
Step 4: Employmentβ
- Company fields show only for employed
- Business fields show only for self-employed
- Fields clear when switching employment status
- Total income calculates from main + additional
Step 5: Additional Informationβ
- Properties array shows only when checkbox checked
- Existing loans array shows only when checkbox checked
- Co-borrowers array shows only when checkbox checked
- Co-borrowers income sums all co-borrower incomes
Cross-Stepβ
- Payment-to-income ratio calculates correctly
- Loan fields disable when age < 18
- Monthly payment revalidates when income changes
- Analytics logs show in console
Debugging Behaviorsβ
If behaviors don't work as expected:
1. Check Console for Errorsβ
// Add debug logging to behaviors
export const loanBehaviorSchema: BehaviorSchemaFn<CreditApplicationForm> = (path) => {
console.log('Registering Step 1 behaviors');
computeFrom(
[path.loanAmount, path.loanTerm, path.interestRate],
path.monthlyPayment,
(values) => {
console.log('Computing monthly payment:', values);
// ... computation
}
);
};
2. Verify Field Pathsβ
Incorrect field paths cause behaviors to silently fail:
// β Wrong - typo in field name
computeFrom([path.loanAmmount], ...);
// β
Correct
computeFrom([path.loanAmount], ...);
3. Check Form Registrationβ
Ensure behaviors are passed to createForm:
// β Forgot to add behaviors
createForm({
schema: creditApplicationSchema,
});
// β
Behaviors registered
createForm({
schema: creditApplicationSchema,
behaviors: creditApplicationBehaviors,
});
4. Verify Component Integrationβ
Make sure you're using the form with behaviors:
function CreditApplicationForm() {
const form = useMemo(() => createCreditApplicationForm(), []); // β Uses behaviors
return <FormField control={form.monthlyPayment} />;
}
Performance Considerationsβ
Behaviors are optimized by ReFormer, but keep these in mind:
1. Avoid Expensive Computationsβ
// β Bad - complex computation on every change
computeFrom([path.data], path.result, (values) => {
return expensiveCalculation(values.data); // Runs on every change
});
// β
Better - debounce or memoize expensive operations
computeFrom([path.data], path.result, (values) => {
return memoizedExpensiveCalculation(values.data);
});
2. Minimize Watch Side Effectsβ
// β Bad - heavy side effect on every change
watch(path.field, (value) => {
makeAPICall(value); // Triggered on every keystroke!
});
// β
Better - debounce API calls
watch(
path.field,
debounce((value) => {
makeAPICall(value);
}, 500)
);
3. Don't Create Circular Dependenciesβ
// β Bad - circular dependency
computeFrom([path.a], path.b, ...);
computeFrom([path.b], path.a, ...); // Infinite loop!
// β
Good - one-way dependencies
computeFrom([path.a, path.b], path.c, ...);
Summaryβ
We've successfully implemented all behaviors for the Credit Application form:
Step 1: Loan Informationβ
- β Interest rate calculation (base + discounts)
- β Monthly payment calculation (annuity formula)
- β Conditional mortgage/car fields
- β Automatic field reset
Step 2: Personal Informationβ
- β Full name generation (Π€ΠΠ format)
- β Age calculation from birth date
Step 3: Contact Informationβ
- β Address copying (registration β residence)
- β Conditional visibility/access
Step 4: Employmentβ
- β Employment-specific fields
- β Total income calculation
- β Field reset on status change
Step 5: Additional Informationβ
- β Conditional arrays (properties, loans, co-borrowers)
- β Co-borrowers income calculation
Cross-Stepβ
- β Payment-to-income ratio
- β Smart revalidation
- β Age-based access control
- β Analytics tracking
Key Achievementsβ
- Declarative Logic - No manual subscriptions, clean code
- Organized Structure - Easy to find and modify behaviors
- Type Safety - Full TypeScript support
- Maintainable - Changes localized to specific files
- Testable - Each behavior can be tested independently
What's Next?β
The form now has sophisticated interactivity, but it still needs validation to ensure data quality. In the next section (Validation), we'll add:
- Built-in validators (required, min, max, email, etc.)
- Conditional validation (rules that depend on other fields)
- Cross-field validation (payment <= 50% income)
- Async validation (server-side checks)
- Array validation (properties, loans, co-borrowers)
The behaviors we've created will work seamlessly with validation rules!