条件字段:动态显示隐藏与验证规则

条件字段:动态显示隐藏与验证规则

【免费下载链接】form 🤖 Powerful and type-safe form state management for the web. TS/JS, React Form, Solid Form, Svelte Form and Vue Form. 【免费下载链接】form 项目地址: https://gitcode.com/GitHub_Trending/form/form

在现代Web应用开发中,表单处理是必不可少的功能。然而,传统的表单库往往难以处理复杂的条件逻辑,比如根据用户选择动态显示或隐藏字段,以及相应的验证规则调整。TanStack Form通过其强大的动态验证机制,为开发者提供了优雅的解决方案。

为什么需要条件字段?

条件字段(Conditional Fields)在实际业务场景中无处不在:

  • 用户类型选择:选择"企业用户"时显示公司信息字段,选择"个人用户"时隐藏
  • 地址信息:选择不同国家时显示对应的省/州字段
  • 支付方式:选择信用卡时显示卡号字段,选择PayPal时隐藏
  • 问卷调查:根据前一个问题的答案决定下一个问题的显示

传统表单库处理这些场景时往往需要手动管理字段状态和验证逻辑,代码复杂度急剧上升。

TanStack Form的动态验证机制

TanStack Form通过validationLogicrevalidateLogic提供了强大的动态验证能力。让我们通过一个完整的示例来理解其工作原理。

基础示例:用户类型选择

import { useForm, revalidateLogic } from '@tanstack/react-form'
import { z } from 'zod'

const userSchema = z.object({
  userType: z.enum(['personal', 'business']),
  firstName: z.string().min(1, '姓名不能为空'),
  lastName: z.string().min(1, '姓氏不能为空'),
  companyName: z.string().optional(),
  taxId: z.string().optional(),
}).refine((data) => {
  if (data.userType === 'business') {
    return data.companyName && data.companyName.length > 0
  }
  return true
}, {
  message: '公司名称不能为空',
  path: ['companyName']
})

function UserForm() {
  const form = useForm({
    defaultValues: {
      userType: 'personal',
      firstName: '',
      lastName: '',
      companyName: '',
      taxId: ''
    },
    validationLogic: revalidateLogic({
      mode: 'change',
      modeAfterSubmission: 'blur'
    }),
    validators: {
      onDynamic: userSchema
    },
    onSubmit: async ({ value }) => {
      console.log('提交数据:', value)
    }
  })

  return (
    <form onSubmit={(e) => {
      e.preventDefault()
      form.handleSubmit()
    }}>
      {/* 用户类型选择 */}
      <form.Field
        name="userType"
        children={(field) => (
          <div>
            <label>用户类型:</label>
            <select
              value={field.state.value}
              onChange={(e) => field.handleChange(e.target.value)}
            >
              <option value="personal">个人用户</option>
              <option value="business">企业用户</option>
            </select>
          </div>
        )}
      />

      {/* 基础信息字段 */}
      <form.Field
        name="firstName"
        children={(field) => (
          <div>
            <label>姓名:</label>
            <input
              value={field.state.value}
              onChange={(e) => field.handleChange(e.target.value)}
            />
            {field.state.meta.errors?.map(err => (
              <span key={err.message}>{err.message}</span>
            ))}
          </div>
        )}
      />

      <form.Field
        name="lastName"
        children={(field) => (
          <div>
            <label>姓氏:</label>
            <input
              value={field.state.value}
              onChange={(e) => field.handleChange(e.target.value)}
            />
          </div>
        )}
      />

      {/* 条件字段:企业信息 */}
      <form.Subscribe
        selector={(state) => state.values.userType}
        children={(userType) => userType === 'business' && (
          <>
            <form.Field
              name="companyName"
              children={(field) => (
                <div>
                  <label>公司名称:</label>
                  <input
                    value={field.state.value}
                    onChange={(e) => field.handleChange(e.target.value)}
                  />
                  {field.state.meta.errors?.map(err => (
                    <span key={err.message}>{err.message}</span>
                  ))}
                </div>
              )}
            />

            <form.Field
              name="taxId"
              children={(field) => (
                <div>
                  <label>税号:</label>
                  <input
                    value={field.state.value}
                    onChange={(e) => field.handleChange(e.target.value)}
                  />
                </div>
              )}
            />
          </>
        )}
      />

      <button type="submit">提交</button>
    </form>
  )
}

验证逻辑的工作原理

验证触发时机

TanStack Form支持多种验证触发模式:

mermaid

动态验证配置

// 灵活的验证配置
const validationConfig = revalidateLogic({
  mode: 'change',          // 初始验证模式
  modeAfterSubmission: 'blur', // 提交后的验证模式
  reValidateOnMount: true, // 挂载时重新验证
  shouldValidate: (cause) => {
    // 自定义验证触发逻辑
    return cause !== 'server' // 排除服务器触发的验证
  }
})

复杂条件验证场景

多级条件依赖

// 地址信息的多级条件验证
const addressSchema = z.object({
  country: z.string().min(1, '请选择国家'),
  province: z.string().optional(),
  state: z.string().optional(),
  city: z.string().min(1, '城市不能为空'),
  zipCode: z.string().min(1, '邮编不能为空')
}).refine((data) => {
  if (data.country === 'CN') {
    return data.province && data.province.length > 0
  } else if (data.country === 'US') {
    return data.state && data.state.length > 0
  }
  return true
}, {
  message: '请选择正确的地区',
  path: ['province']
})

// 在组件中使用
<form.Subscribe
  selector={(state) => state.values.country}
  children={(country) => (
    <>
      {country === 'CN' && (
        <form.Field
          name="province"
          children={(field) => (
            <select
              value={field.state.value}
              onChange={(e) => field.handleChange(e.target.value)}
            >
              <option value="">请选择省份</option>
              <option value="beijing">北京</option>
              <option value="shanghai">上海</option>
              {/* 更多省份 */}
            </select>
          )}
        />
      )}
      
      {country === 'US' && (
        <form.Field
          name="state"
          children={(field) => (
            <select
              value={field.state.value}
              onChange={(e) => field.handleChange(e.target.value)}
            >
              <option value="">请选择州</option>
              <option value="california">加利福尼亚</option>
              <option value="new_york">纽约</option>
              {/* 更多州 */}
            </select>
          )}
        />
      )}
    </>
  )}
/>

异步条件验证

// 异步验证示例:检查用户名是否可用
const asyncValidationSchema = z.object({
  username: z.string()
    .min(3, '用户名至少3个字符')
    .refine(async (username) => {
      if (username.length < 3) return true
      const response = await fetch(`/api/check-username?username=${username}`)
      const data = await response.json()
      return data.available
    }, {
      message: '用户名已被占用'
    }),
  email: z.string().email('请输入有效的邮箱')
})

// 使用异步验证
const form = useForm({
  defaultValues: { username: '', email: '' },
  validationLogic: revalidateLogic(),
  validators: {
    onDynamicAsync: asyncValidationSchema
  }
})

最佳实践与性能优化

1. 条件字段的性能考虑

// 使用React.memo优化条件渲染
const BusinessFields = React.memo(() => (
  <>
    <form.Field name="companyName" /* ... */ />
    <form.Field name="taxId" /* ... */ />
  </>
))

// 在组件中使用
<form.Subscribe
  selector={(state) => state.values.userType}
  children={(userType) => userType === 'business' && <BusinessFields />}
/>

2. 验证规则的模块化

// 将验证规则拆分为可重用的模块
export const userValidations = {
  personal: z.object({
    firstName: z.string().min(1, '姓名不能为空'),
    lastName: z.string().min(1, '姓氏不能为空')
  }),
  business: z.object({
    companyName: z.string().min(1, '公司名称不能为空'),
    taxId: z.string().min(10, '税号格式不正确')
  })
}

// 组合验证规则
const createUserSchema = (userType: string) => {
  const baseSchema = z.object({
    userType: z.string(),
    email: z.string().email()
  })
  
  if (userType === 'business') {
    return baseSchema.merge(userValidations.business)
  }
  return baseSchema.merge(userValidations.personal)
}

3. 错误处理与用户体验

// 增强的错误显示组件
function EnhancedFieldError({ field, showTouchedOnly = true }) {
  if (showTouchedOnly && !field.state.meta.isTouched) {
    return null
  }
  
  return (
    <div className="error-container">
      {field.state.meta.errors?.map((error, index) => (
        <div key={index} className="error-message">
          {error.message}
        </div>
      ))}
      {field.state.meta.isValidating && (
        <div className="validating">验证中...</div>
      )}
    </div>
  )
}

// 使用示例
<form.Field
  name="username"
  children={(field) => (
    <div>
      <label>用户名</label>
      <input
        value={field.state.value}
        onChange={(e) => field.handleChange(e.target.value)}
        onBlur={field.handleBlur}
      />
      <EnhancedFieldError field={field} />
    </div>
  )}
/>

总结表格:条件字段处理策略

场景解决方案优势注意事项
简单条件显示form.Subscribe + 条件渲染代码简洁,易于理解避免过度嵌套
复杂条件验证Zod refine + 自定义逻辑验证逻辑集中管理保持refine逻辑简洁
异步条件验证异步refine + 防抖实时反馈用户体验好需要处理加载状态
多级条件依赖分层Subscribe逻辑清晰,易于维护注意性能优化
动态字段数组FieldArray + 条件验证处理动态生成的字段需要管理字段索引

TanStack Form的条件字段处理机制为开发者提供了强大而灵活的工具,能够优雅地处理各种复杂的业务场景。通过合理的架构设计和性能优化,可以构建出既功能强大又用户体验优秀的表单应用。

记住关键原则:保持验证逻辑的集中管理,合理使用条件渲染优化性能,以及提供清晰的用户反馈。这样就能打造出专业级的条件表单体验。

【免费下载链接】form 🤖 Powerful and type-safe form state management for the web. TS/JS, React Form, Solid Form, Svelte Form and Vue Form. 【免费下载链接】form 项目地址: https://gitcode.com/GitHub_Trending/form/form

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值