Antd3中动态校验规则踩坑

场景描述

下图所示表单有三个字段,“跳转文案”和“跳转链接”只要填写了其中一个这两个字段就要变成必填,反之,两个都为空的时候两个字段都变成非必填;简单描述就是要吗都填写,要吗都不填写

image.png

如下图所示,校验规则是要基于两个字段的值动态的生成的;也就包含了自己修改了也要动态的修改自己是否为必填

image.png

解决方案

尝试过使用下面的方案

  1. 在state存储上面的两个字段的值,然后计算是否必填然后赋值给表单的校验规则选项
  2. 在渲染的时候通过 getFieldValue 获取表单填写的数据,然后计算是否必填然后赋值给表单的校验规则选项

上面的实现类似,只是方案2少了state存储,相对要简洁一点;虽然UI展示上前面的会根据计算后的值展示和隐藏,但是表单的校验还是没有生效;因为默认修改内容会触发校验,就会出现问题,究其原因如下:

因为数据变更会动态的修改校验规则,例如原本有内容,删除内容校验规则应该变成“非必填”,但是删除内容的时候校验规则还是使用之前该字段非空状态的值计算的,也就是必填。即使现在内容为空了,校验就会显示“该字段是必填字段”。

本次解决方案

关闭自带的校验,文本修改后,进行setTimeout手动校验,实现部分代码如下:

import React, { FC, memo, useCallback, useEffect } from 'react'
import { Button, Form, Input } from 'antd'
import ValidLinkInput from '@/components/ValidLinkInput'
import { FormComponentProps } from 'antd/lib/form'

interface IValue {
  title: string
  subtitle: string
  url: string
}

interface IProps extends FormComponentProps {
  onConfirm: (value: IValue) => void
  value: IValue
}

const AppCardConfig: FC<IProps> = memo(props => {
  const {
    form: { getFieldDecorator, getFieldValue, setFieldsValue, validateFields },
    onConfirm,
    value,
  } = props
  /**
   * 处理顶部传入的数据
   */
  useEffect(() => {
    if (value) {
      setFieldsValue(value)
    }
  }, [value, setFieldsValue])
  /**
   * 上交数据
   */
  const handleConfirm = useCallback(() => {
    validateFields((err, formData: IValue) => {
      if (!err) {
        onConfirm({ ...value, ...formData })
      }
    })
  }, [value, onConfirm, validateFields])

  // 获取表单数据
  const subTitle = getFieldValue('subtitle')
  const url = getFieldValue('url')

  // 计算是否必填
  const isRequired = !!subTitle || !!url

  return (
    <Form>
      <div className="card-title">卡片配置</div>
      <Form.Item label="标题">
        {getFieldDecorator('title')(<Input placeholder="请输入标题" />)}
      </Form.Item>
      <Form.Item label="跳转⽂案" required={isRequired}>
        {getFieldDecorator('subtitle', {
          // 屏蔽自动校验
          validateTrigger: '',
          rules: [{ required: isRequired, message: '跳转文案必填' }],
        })(
          <Input
            placeholder="请输入跳转⽂案"
            onChange={() => {
              setTimeout(() => {
                validateFields(['subtitle', 'url'], { force: true })
              }, 100)
            }}
          />
        )}
      </Form.Item>
      <Form.Item label="跳转链接" required={isRequired}>
        {getFieldDecorator('url', {
          // 屏蔽自动校验
          validateTrigger: '',
          rules: [{ required: isRequired, message: '跳转链接必填' }],
        })(
          <ValidLinkInput
            autoValidate
            onChange={() => {
              setTimeout(() => {
                validateFields(['subtitle', 'url'], { force: true })
              }, 100)
            }}
          />
        )}
      </Form.Item>
      <Form.Item>
        <Button onClick={handleConfirm}>保存修改</Button>
      </Form.Item>
    </Form>
  )
})
export default Form.create<IProps>({})(AppCardConfig)

注意: 这里的100ms在有些慢的浏览器上可能会有些问题,但是目前在macOS 12的Chrome 97上没有发现问题;

参考文档

https://3x.ant.design/components/form-cn/#components-form-demo-dynamic-rule

留下回复