advanced formily patterns

Advanced Formily Patterns

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "advanced formily patterns" with this command: npx skills add whinc/my-claude-plugins/whinc-my-claude-plugins-advanced-formily-patterns

Advanced Formily Patterns

This skill provides advanced guidance for implementing complex form scenarios with Formily. Focus on dynamic forms, conditional fields, nested forms, async validation, performance optimization, and sophisticated form patterns in TypeScript.

Dynamic Forms

Conditional Field Visibility

import { createForm, onFieldChange } from '@formily/core'

const form = createForm({ effects() { // Show/hide fields based on other field values onFieldChange('userType', ['value'], (field) => { const userType = field.value

  form.setFieldState('companyName', state => {
    state.visible = userType === 'business'
    if (userType !== 'business') {
      state.value = ''
    }
  })

  form.setFieldState('studentId', state => {
    state.visible = userType === 'student'
    if (userType !== 'student') {
      state.value = ''
    }
  })
})

} })

Dynamic Field Options

const form = createForm({ effects() { onFieldChange('country', ['value'], (field) => { const country = field.value

  // Update city options based on selected country
  const cityOptions = country === 'us'
    ? [
        { label: 'New York', value: 'nyc' },
        { label: 'Los Angeles', value: 'la' },
        { label: 'Chicago', value: 'chi' }
      ]
    : country === 'uk'
    ? [
        { label: 'London', value: 'lon' },
        { label: 'Manchester', value: 'man' },
        { label: 'Birmingham', value: 'bir' }
      ]
    : []

  form.setFieldState('city', state => {
    state.dataSource = cityOptions
    state.value = cityOptions.length > 0 ? cityOptions[0].value : ''
  })
})

} })

Dynamic Form Schema

import { createForm, Form } from '@formily/core'

interface DynamicFormConfig { fields: Array<{ name: string type: 'string' | 'number' | 'boolean' | 'array' | 'object' label: string required?: boolean options?: Array<{ label: string; value: any }> validation?: any[] }> }

const createDynamicForm = (config: DynamicFormConfig) => { const schema = { type: 'object', properties: config.fields.reduce((acc, field) => { acc[field.name] = { type: field.type, title: field.label, required: field.required, 'x-validator': field.validation }

  if (field.type === 'boolean') {
    acc[field.name]['x-component'] = 'Checkbox'
  } else if (field.options) {
    acc[field.name]['x-component'] = 'Select'
    acc[field.name]['x-component-props'] = {
      options: field.options
    }
  } else {
    acc[field.name]['x-component'] = 'Input'
  }

  return acc
}, {} as any)

}

return createForm({ schema }) }

Array Field Patterns

Dynamic Array Items

import { createForm, isArrayField } from '@formily/core' import { ArrayField } from '@formily/react'

const form = createForm({ initialValues: { contacts: [ { name: '', phone: '', relationship: '' } ] } })

// Add new contact item const addContact = () => { form.pushValues('contacts', { name: '', phone: '', relationship: '' }) }

// Remove contact item const removeContact = (index: number) => { form.removeValues(contacts.${index}) }

// Reorder contacts const moveContact = (fromIndex: number, toIndex: number) => { const field = form.query('contacts') if (isArrayField(field)) { field.move(fromIndex, toIndex) } }

Nested Array Objects

interface Contact { name: string emails: Array<{ address: string type: 'work' | 'personal' | 'other' }> }

const NestedArrayForm = () => { const form = createForm<Contact>({ initialValues: { name: '', emails: [] } })

const addEmail = () => { form.pushValues('emails', { address: '', type: 'work' }) }

return ( <FormProvider form={form}> <ArrayField name="emails"> {(field) => ( <div> {field.value?.map((email, index) => ( <div key={index}> <Field name={emails.${index}.address}> {/** Email input component /} </Field> <Field name={emails.${index}.type}> {/* Email type select component */} </Field> </div> ))} <button onClick={addEmail}>Add Email</button> </div> )} </ArrayField> </FormProvider> ) }

Async Validation Patterns

Server-Side Validation

import { createForm } from '@formily/core'

const form = createForm({ effects() { // Async email uniqueness check onFieldChange('email', ['value'], async (field) => { if (!field.value) return

  field.validating = true

  try {
    const isUnique = await checkEmailUniqueness(field.value)

    if (!isUnique) {
      field.errors = ['Email already exists']
    } else {
      field.errors = []
    }
  } catch (error) {
    field.errors = ['Validation failed. Please try again.']
  } finally {
    field.validating = false
  }
})

// Debounced validation for performance
onFieldChange('username', ['value'],
  debounce(async (field) => {
    if (!field.value || field.value.length &#x3C; 3) return

    field.validating = true

    try {
      const exists = await checkUsernameExists(field.value)
      field.errors = exists ? ['Username already taken'] : []
    } catch (error) {
      field.errors = ['Unable to validate username']
    } finally {
      field.validating = false
    }
  }, 500)
)

} })

// Helper function for debouncing const debounce = <T extends (...args: any[]) => any>( func: T, wait: number ) => { let timeout: NodeJS.Timeout return (...args: Parameters<T>) => { clearTimeout(timeout) timeout = setTimeout(() => func(...args), wait) } }

Complex Validation Rules

import { createForm, onFieldReact } from '@formily/core'

const form = createForm({ effects() { // Password strength validation onFieldReact('password', (field) => { const password = field.value || '' const errors = []

  if (password.length &#x3C; 8) {
    errors.push('Password must be at least 8 characters')
  }

  if (!/[A-Z]/.test(password)) {
    errors.push('Password must contain uppercase letter')
  }

  if (!/[a-z]/.test(password)) {
    errors.push('Password must contain lowercase letter')
  }

  if (!/\d/.test(password)) {
    errors.push('Password must contain number')
  }

  if (!/[!@#$%^&#x26;*]/.test(password)) {
    errors.push('Password must contain special character')
  }

  field.errors = errors
})

// Confirm password validation
onFieldReact('confirmPassword', (field) => {
  const password = form.values.password
  const confirmPassword = field.value

  if (confirmPassword &#x26;&#x26; password !== confirmPassword) {
    field.errors = ['Passwords do not match']
  } else {
    field.errors = []
  }
})

// Date range validation
onFieldReact('endDate', (field) => {
  const startDate = form.values.startDate
  const endDate = field.value

  if (startDate &#x26;&#x26; endDate &#x26;&#x26; new Date(endDate) &#x3C;= new Date(startDate)) {
    field.errors = ['End date must be after start date']
  } else {
    field.errors = []
  }
})

} })

Nested Form Patterns

Multi-Step Forms

import { createForm } from '@formily/core' import { useState } from 'react'

interface MultiStepFormData { personal: { firstName: string lastName: string email: string } professional: { company: string position: string experience: number } preferences: { notifications: boolean theme: 'light' | 'dark' language: string } }

const MultiStepForm = () => { const [currentStep, setCurrentStep] = useState(0)

const form = createForm<MultiStepFormData>({ initialValues: { personal: { firstName: '', lastName: '', email: '' }, professional: { company: '', position: '', experience: 0 }, preferences: { notifications: true, theme: 'light', language: 'en' } } })

const steps = [ { title: 'Personal Info', fields: ['personal'] }, { title: 'Professional', fields: ['professional'] }, { title: 'Preferences', fields: ['preferences'] } ]

const handleNext = async () => { // Validate current step const currentFields = steps[currentStep].fields const isValid = await Promise.all( currentFields.map(field => form.validate(field)) )

if (isValid.every(result => result === null)) {
  if (currentStep &#x3C; steps.length - 1) {
    setCurrentStep(currentStep + 1)
  }
}

}

const handlePrevious = () => { if (currentStep > 0) { setCurrentStep(currentStep - 1) } }

const handleSubmit = async () => { try { await form.validate() const values = form.values console.log('Form submitted:', values) } catch (errors) { console.error('Validation errors:', errors) } }

return ( <FormProvider form={form}> {/* Step content based on currentStep /} {/ Navigation buttons */} </FormProvider> ) }

Nested Object Management

interface Address { street: string city: string state: string zip: string country: string }

interface UserProfile { name: string email: string address: Address emergencyContact: { name: string relationship: string phone: string } }

const NestedForm = () => { const form = createForm<UserProfile>({ initialValues: { name: '', email: '', address: { street: '', city: '', state: '', zip: '', country: 'us' }, emergencyContact: { name: '', relationship: '', phone: '' } } })

// Update nested object efficiently const updateAddress = (field: keyof Address, value: string) => { form.setFieldState(address.${field}, state => { state.value = value }) }

// Batch update nested object const updateEntireAddress = (address: Partial<Address>) => { form.setValues({ address: { ...form.values.address, ...address } }) }

return ( <FormProvider form={form}> {/* Nested form fields */} </FormProvider> ) }

Performance Optimization

Memoized Field Components

import React, { memo } from 'react' import { observer } from '@formily/reactive-react' import { useField } from '@formily/react'

interface OptimizedFieldProps { name: string component: React.ComponentType<any> [key: string]: any }

const OptimizedField = observer( memo<OptimizedFieldProps>(({ name, component: Component, ...props }) => { const field = useField(name)

return (
  &#x3C;div>
    &#x3C;Component {...props} value={field.value} onChange={field.onInput} />
    {field.errors &#x26;&#x26; (
      &#x3C;div className="error">{field.errors[0]}&#x3C;/div>
    )}
  &#x3C;/div>
)

}) )

// Usage const MyForm = () => { return ( <FormProvider form={form}> <OptimizedField name="firstName" component={Input} /> <OptimizedField name="email" component={Input} /> </FormProvider> ) }

Virtualized Large Forms

import { FixedSizeList as List } from 'react-window'

const VirtualizedForm = () => { const [items] = useState(() => Array.from({ length: 1000 }, (_, i) => ({ id: field-${i}, name: Field ${i + 1}, value: '' })) )

const Row = ({ index, style }: { index: number; style: React.CSSProperties }) => ( <div style={style}> <Field name={items.${index}.value}> {({ field, state }) => ( <div> <label>{items[index].name}</label> <Input value={state.value} onChange={(e) => field.onInput(e.target.value)} /> </div> )} </Field> </div> )

return ( <FormProvider form={form}> <List height={400} itemCount={items.length} itemSize={60} width="100%" > {Row} </List> </FormProvider> ) }

Efficient Field Updates

// Batch multiple field updates const batchUpdateForm = () => { form.batch(() => { form.setValues({ name: 'John' }) form.setValues({ email: 'john@example.com' }) form.setValues({ age: 30 }) form.setFieldState('name', state => { state.touched = true }) }) }

// Selective re-rendering const SelectiveForm = () => { const [shouldRenderExpensive, setShouldRenderExpensive] = useState(false)

return ( <FormProvider form={form}> {/* Always render essential fields /} <Field name="name"> {/ Simple input */} </Field>

  {/* Conditionally render expensive fields */}
  {shouldRenderExpensive &#x26;&#x26; (
    &#x3C;Field name="complexField">
      {/* Expensive component */}
    &#x3C;/Field>
  )}

  &#x3C;button onClick={() => setShouldRenderExpensive(!shouldRenderExpensive)}>
    Toggle Complex Field
  &#x3C;/button>
&#x3C;/FormProvider>

) }

Custom Field Patterns

Address Field Component

interface AddressFieldProps { name: string label?: string required?: boolean }

const AddressField: React.FC<AddressFieldProps> = ({ name, label, required }) => { const form = useForm()

const handleCountryChange = (country: string) => { // Reset dependent fields when country changes form.setValues({ [${name}.state]: '', [${name}.city]: '' }) }

return ( <div> <h3>{label || 'Address'}</h3>

  &#x3C;Row gutter={16}>
    &#x3C;Col span={24}>
      &#x3C;Field name={`${name}.street`}>
        &#x3C;FormItem label="Street Address" required={required}>
          &#x3C;Input placeholder="123 Main St" />
        &#x3C;/FormItem>
      &#x3C;/Field>
    &#x3C;/Col>
  &#x3C;/Row>

  &#x3C;Row gutter={16}>
    &#x3C;Col span={8}>
      &#x3C;Field name={`${name}.city`}>
        &#x3C;FormItem label="City" required={required}>
          &#x3C;Input placeholder="City" />
        &#x3C;/FormItem>
      &#x3C;/Field>
    &#x3C;/Col>

    &#x3C;Col span={8}>
      &#x3C;Field name={`${name}.state`}>
        &#x3C;FormItem label="State/Province">
          &#x3C;Input placeholder="State" />
        &#x3C;/FormItem>
      &#x3C;/Field>
    &#x3C;/Col>

    &#x3C;Col span={8}>
      &#x3C;Field name={`${name}.zip`}>
        &#x3C;FormItem label="ZIP/Postal Code">
          &#x3C;Input placeholder="ZIP" />
        &#x3C;/FormItem>
      &#x3C;/Field>
    &#x3C;/Col>
  &#x3C;/Row>

  &#x3C;Row gutter={16}>
    &#x3C;Col span={24}>
      &#x3C;Field name={`${name}.country`}>
        &#x3C;FormItem label="Country" required={required}>
          &#x3C;Select
            placeholder="Select country"
            onChange={handleCountryChange}
          >
            &#x3C;Select.Option value="us">United States&#x3C;/Select.Option>
            &#x3C;Select.Option value="ca">Canada&#x3C;/Select.Option>
            &#x3C;Select.Option value="uk">United Kingdom&#x3C;/Select.Option>
          &#x3C;/Select>
        &#x3C;/FormItem>
      &#x3C;/Field>
    &#x3C;/Col>
  &#x3C;/Row>
&#x3C;/div>

) }

File Upload with Progress

import { Upload, Button, Progress } from 'antd'

interface FileUploadFieldProps { name: string maxSize?: number accept?: string multiple?: boolean }

const FileUploadField: React.FC<FileUploadFieldProps> = ({ name, maxSize = 5 * 1024 * 1024, // 5MB accept, multiple = false }) => { const [uploadProgress, setUploadProgress] = useState<Record<string, number>>({})

const handleUpload = async (file: File) => { const formData = new FormData() formData.append('file', file)

try {
  // Simulate upload progress
  let progress = 0
  const interval = setInterval(() => {
    progress += 10
    setUploadProgress(prev => ({ ...prev, [file.name]: progress }))

    if (progress >= 100) {
      clearInterval(interval)
    }
  }, 200)

  const response = await uploadFile(formData)

  // Update form field with upload result
  form.setValues({
    [name]: response.url
  })

  return response
} catch (error) {
  console.error('Upload failed:', error)
  throw error
}

}

return ( <Field name={name}> {({ field, state }) => ( <div> <Upload customRequest={({ file }) => handleUpload(file as File)} beforeUpload={(file) => { if (file.size > maxSize) { alert(File size must be less than ${maxSize / 1024 / 1024}MB) return false } return true }} accept={accept} multiple={multiple} > <Button icon={<UploadOutlined />}> Select {multiple ? 'Files' : 'File'} </Button> </Upload>

      {Object.entries(uploadProgress).map(([filename, progress]) => (
        &#x3C;div key={filename}>
          &#x3C;span>{filename}&#x3C;/span>
          &#x3C;Progress percent={progress} size="small" />
        &#x3C;/div>
      ))}

      {state.value &#x26;&#x26; (
        &#x3C;div>Uploaded: {state.value}&#x3C;/div>
      )}
    &#x3C;/div>
  )}
&#x3C;/Field>

) }

Additional Resources

Example Files

  • examples/dynamic-form.tsx

  • Complete dynamic form implementation

  • examples/performance-optimization.tsx

  • Performance optimization techniques

  • examples/complex-validation.tsx

  • Advanced validation patterns

Reference Files

  • references/performance-checklist.md

  • Performance optimization checklist

  • references/validation-patterns.md

  • Complex validation pattern reference

  • references/nested-form-patterns.md

  • Nested form architecture guide

Common Advanced Patterns

  • Dynamic field visibility and options

  • Async validation with debouncing

  • Nested object and array management

  • Multi-step form workflows

  • Performance optimization strategies

  • Custom field component creation

Best Practices

  • Use debouncing for async validation to prevent excessive API calls

  • Batch field updates using form.batch() for better performance

  • Memoize expensive components to prevent unnecessary re-renders

  • Virtualize large forms with hundreds of fields

  • Use TypeScript generics for type-safe nested forms

  • Implement proper error boundaries for complex form scenarios

  • Optimize field dependencies to minimize validation triggers

  • Use form effects for reactive field behavior instead of manual listeners

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

taro documentation

No summary provided by upstream source.

Repository SourceNeeds Review
General

ahooks

No summary provided by upstream source.

Repository SourceNeeds Review
General

formily core fundamentals

No summary provided by upstream source.

Repository SourceNeeds Review