Ant-design: why we always need `callback()` in validator function at Form component? 为什么表单验证的 rules 中使用 validator 时,其方法体总是需要调用 callback()

Created on 3 Mar 2017  ·  38Comments  ·  Source: ant-design/ant-design

感谢提供 Form 等一系列组件。

问题描述

由于要在提交前,需要对表单内的各个 field 进行再次校验,使用了 validateFieldsAndScroll。但发现并未生效。

后来经调试,发现如果对该函数传入一组指定的 field,是可以生效的。但只要这组 fileds 中,包含了任意一个在校验规则中指定使用 validator 辅助校验的,则整个 validateFields 方法都不生效。

最后,才发现,是因为,我一开始并没有在对应的 validator 回调中,总是返回 callback() (觉得很多余,而且一开始不写也可以对单个 input 做正常校验)。加上以后就 ok 了。

疑惑

请问,这属于 bug 吗? 因为文档中并没有指出,validator 中必须要返回 callback()
如果确实是必须要返回 callback ,建议要在文档中提醒开发者注意,或者默认可以缺省。

代码片段

handleSubmit = (e) => {
        e.preventDefault()

        const { onSubmit } = this.props
        const { validateFieldsAndScroll, getFieldsValue, resetFields } = this.props.form

        validateFieldsAndScroll((err, values) => {
              if (!err) {
                  onSubmit(getFieldsValue())
              }
        })
}
handleConfirmPassword = (rule, value, callback) => {
        const { getFieldValue } = this.props.form
        if (value && value !== getFieldValue('newPassword')) {
            callback('两次输入不一致!')
        }

        // Note: 必须总是返回一个 callback,否则 validateFieldsAndScroll 无法响应
        callback()
 }

render()

<FormItem
    {...formItemLayout}
    label="确认密码"
>
{
    getFieldDecorator('confirmPassword', {
           rules: [{
                  required: true,
                  message: '请再次输入以确认新密码',
            }, {
                  validator: this.handleConfirmPassword
            }],
    })(<Input type="password" />)
}
</FormItem>

Environment

  • antd version: 最新
  • OS and its version: OS X
  • Browser and its version: chrome 最新
Usage ❓FAQ 🙅🏻‍♀️ WON'T RESOLVE

Most helpful comment

被人吐槽了 https://www.zhihu.com/question/33629737/answer/150154145

可以加一个文档说明,我们做开源的原则之一是兵来将挡水来土掩。

All 38 comments

It's by designed: https://github.com/yiminghe/async-validator/

BTW, how does async-validator know an asynchronous procedure is end, if you don't use callback to notify it?

被人吐槽了 https://www.zhihu.com/question/33629737/answer/150154145

可以加一个文档说明,我们做开源的原则之一是兵来将挡水来土掩。

文档已更新。

你好是否 这种用法 只能用 class 的写法?
能否用 纯函数的 写法 写 React Component .如果有,有没有案例啊?

这个API设计真的是很不优雅,感受下我只是想做一下某个checkbox是否选中的检查:

rules: [
            {
              message: 'You need to agree to our terms to sign up.',
              validator: (rule, value, cb) => (value === true ? cb() : cb(true)),
            },
          ],

并且给到callback的第一个参数是错误信息,和message也是冗余(虽然似乎只要返回任何东西都会使用message了,应该是message 覆盖了错误信息)。

大部分使用者预期的使用方式应该是直接返回true或者false来决定对错:

rules: [
            {
              message: 'You need to agree to our terms to sign up.',
              validator: (rule, value, cb) => value === true,
            },
          ],

可用,但是还是建议优化这个API,即使用的是 https://github.com/yiminghe/async-validator/,但是ANT这边完成全可以在调用上封装一下

@neekey 可以去给 async-validator 提个兼容的 PR

@neekey 这种奇怪的接口是为了实现:

  • 异步的validation
  • 支持不同的错误返回不同的message

有没有更好的方式啊?

<Row>
                <Col span={24} key={"method"}>
                    <FormItem {...formItemMethodLayout} label="请求方法">
                        {getFieldDecorator("method", {
                            initialValue: this.state.data.method,
                            validateTrigger: "onChange",
                            rules: [{
                                required: true,
                                message: `请选择请求方法`
                            }, {
                                //validator: this.state.checkUnique ? this.checkConfirmMethod.bind(this) : (rule, value, callback) => {callback();},
                                validator: (这如何判断当前组件是否开始校验了) ?
 this.checkConfirmMethod.bind(this) : (如果不是当前组件开始校验,就调用其他函数),
                            }]
                        })(
                            <Select placeholder={"请选择请求方法"} onChange={::this.handleChange.bind(this)}>
                                {this.state.apiRequestMethodSelect}
                            </Select>
                        )}
                    </FormItem>
                </Col>
            </Row

每次碰到函数的校验,都会来这里看一次,真的不好记

validator 函数

startNumValidator = (rule, value, callback) => {
    const { exportDataCount } = this.props;
    const reg = /^[1-9][0-9]*$/;
    let errors = [];
    if (!reg.test(value) || value > exportDataCount) {
      errors.push(new Error('illegal value'));
    }
    callback(errors);
  }

不是统一callback errors么,也挺合理的啊感觉
不知道新版的antd开始区分异步和同步校验了没? validator(同步) asyncValidator(异步)

在请求后,后台返回的回调里面怎么调用这个消息提示

在使用rule中validator 做自定义校验,遇到一个问题
validateFunc = (rule, value, callback) => {
if (true) {
console.log(11);
callback('Error message');
return;
}
callback();
};
我想要校验结果'Error message'在对应的FormItem组件下显示,但是callback执行后,'Error message'没有在对应的FormItem组件下显示,而是在控制台打印出来,这是为什么?

在对应的FormItem组件下显示的逻辑是怎么书写的?

@neekey 可以去给 async-validator 提个兼容的 PR

有人提了,yiminghe说,反对。callback 更直接。都9102年了,还用callback,坐等真香

在对应的FormItem组件下显示的逻辑是怎么书写的?

我把自定义校验的方案改了,在onFieldsChange()中判断,不符合自定义的规则就直接改errors,然后就会在控件中显示error信息
onFieldsChange(props, changedFields) { const { dispatch, detail } = props; const finalChangedFields = { ...changedFields }; // 校验时间大小是否符合规则 if ( _.has(changedFields, 'currentDate') && detail?.targetDate && compareCurrentTimeEarlierThanTargetTime( finalChangedFields.currentDate, detail?.targetDate ) ) { finalChangedFields.currentDate.errors = [ { message: 'current Date should be latter than target Date', field: 'currentDate', }, ]; } dispatch({ type: 'controllerModel/saveDetail', payload: { changedFields: finalChangedFields, data: detail, }, }); }

校验通过后下面的提示不消失是怎么回事

问一个问题,这个validator能不能在页面加载的时候,不做校验?

@neekey
must call a callback feature is a big drawback in async situation:

<Form>
          <Form.Item ...>
            {getFieldDecorator('name', {validator: (rule, value, callback) => setTimeout(callback(), 1000)})(
                <Input placeholder="name" />
            )}
          </Form.Item>
....

and before the setTimeout is fired, the whole component is unmounted. Then the timeout is fired, its callback is fired and

component will use setState(I guess) which will result in the following error:

Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount method.

Sadly the callback is not cancellable ! No way to avoid this error!

@jiufengdadi
I encountered this problem.
My solution is pass undefined into the callback. The validator will pass when the first parameter of the callback is an empty array. And it set a default value to the parameter, so undefined is also work.

the callback definition: L146

为什么我自定义验证通过了 还出现async-validator: [""] ,

如果有两个校验的话,第一个会失效,为什么?
```
rules: [
{
required: true,
message: '请输入手机号',
},
{
validator: (rule, value, callback) => {
try {
if (this.props.form.getFieldValue('prefix') == 86 && value) {
const reg = /^1d{10}$/;
if (!reg.test(value)) {
throw new Error('Something wrong!');
}
}
} catch (err) {
callback(err);
}
},
message: '请输入有效手机号',
},
],

 rules: [
                {
                  required: true,
                  message: '请输入手机号',
                },
                {
                  validator: (rule, value, callback) => {
                    try {
                      if (this.props.form.getFieldValue('prefix') == 86 && value) {
                        const reg = /^1\d{10}$/;
                        if (!reg.test(value)) {
                          throw new Error('Something wrong!');
                        }
                      }
                    } catch (err) {
                      callback(err);
                       return // +
                    }
                     callback() // +
                  },
                  message: '请输入有效手机号',
                },
              ],

在使用rule中validator 做自定义校验,遇到一个问题
validateFunc = (rule, value, callback) => {
if (true) {
console.log(11);
callback('Error message');
return;
}
callback();
};
我想要校验结果'Error message'在对应的FormItem组件下显示,但是callback执行后,'Error message'没有在对应的FormItem组件下显示,而是在控制台打印出来,这是为什么?

在使用rule中validator 做自定义校验,遇到一个问题
validateFunc = (rule, value, callback) => {
if (true) {
console.log(11);
callback('Error message');
return;
}
callback();
};
我想要校验结果'Error message'在对应的FormItem组件下显示,但是callback执行后,'Error message'没有在对应的FormItem组件下显示,而是在控制台打印出来,这是为什么?

你解决了吗,遇到同个情况

 rules: [
                {
                  required: true,
                  message: '请输入手机号',
                },
                {
                  validator: (rule, value, callback) => {
                    try {
                      if (this.props.form.getFieldValue('prefix') == 86 && value) {
                        const reg = /^1\d{10}$/;
                        if (!reg.test(value)) {
                          throw new Error('Something wrong!');
                        }
                      }
                    } catch (err) {
                      callback(err);
                       return // +
                    }
                     callback() // +
                  },
                  message: '请输入有效手机号',
                },
              ],

message提示不会重复吗?'请输入手机号 请输入有效手机号' 类似这样

 rules: [
                {
                  required: true,
                  message: '请输入手机号',
                },
                {
                  validator: (rule, value, callback) => {
                    try {
                      if (this.props.form.getFieldValue('prefix') == 86 && value) {
                        const reg = /^1\d{10}$/;
                        if (!reg.test(value)) {
                          throw new Error('Something wrong!');
                        }
                      }
                    } catch (err) {
                      callback(err);
                       return // +
                    }
                     callback() // +
                  },
                  message: '请输入有效手机号',
                },
              ],

message提示不会重复吗?'请输入手机号 请输入有效手机号' 类似这样

不会

image

做如下操纵
image

为什么红色的校验还不消失呢???都满足规格了 这种相互影响的校验怎么做???

rule的validator 成功失败与否都要调用callback,区别是传不传参数

在使用rule中validator 做自定义校验,遇到一个问题
validateFunc = (rule, value, callback) => {
if (true) {
console.log(11);
callback('Error message');
return;
}
callback();
};
我想要校验结果'Error message'在对应的FormItem组件下显示,但是callback执行后,'Error message'没有在对应的FormItem组件下显示,而是在控制台打印出来,这是为什么?

在使用rule中validator 做自定义校验,遇到一个问题
validateFunc = (rule, value, callback) => {
if (true) {
console.log(11);
callback('Error message');
return;
}
callback();
};
我想要校验结果'Error message'在对应的FormItem组件下显示,但是callback执行后,'Error message'没有在对应的FormItem组件下显示,而是在控制台打印出来,这是为什么?

你解决了吗,遇到同个情况

我也遇到了这个问题, 如果initialvalue为空, 在正常输入内容后通过onblur时间可以正常触发自定义校验器并有错误的情况下会进行提示, 但一旦初始initialvalue是有值的情况下, 在validatefields 进行所有表单组件统一校验时, err打印出来是null, 过了一会后 又会在控制台弹出校验失败, 自定义验证器是异步action

我觉得你把函数里的return;删掉,再试一试,或许有用。

------------------ 原始邮件 ------------------
发件人: "Ivan-hl"<[email protected]>;
发送时间: 2019年11月20日(星期三) 下午5:19
收件人: "ant-design/ant-design"<[email protected]>;
抄送: "wk"<[email protected]>; "Manual"<[email protected]>;
主题: Re: [ant-design/ant-design] why we always need callback() in validator function at Form component? 为什么表单验证的 rules 中使用 validator 时,其方法体总是需要调用 callback() (#5155)

在使用rule中validator 做自定义校验,遇到一个问题
validateFunc = (rule, value, callback) => {
if (true) {
console.log(11);
callback('Error message');
return;
}
callback();
};
我想要校验结果'Error message'在对应的FormItem组件下显示,但是callback执行后,'Error message'没有在对应的FormItem组件下显示,而是在控制台打印出来,这是为什么?

在使用rule中validator 做自定义校验,遇到一个问题
validateFunc = (rule, value, callback) => {
if (true) {
console.log(11);
callback('Error message');
return;
}
callback();
};
我想要校验结果'Error message'在对应的FormItem组件下显示,但是callback执行后,'Error message'没有在对应的FormItem组件下显示,而是在控制台打印出来,这是为什么?

你解决了吗,遇到同个情况

我也遇到了这个问题, 如果initialvalue为空, 在正常输入内容后通过onblur时间可以正常触发自定义校验器并有错误的情况下会进行提示, 但一旦初始initialvalue是有值的情况下, 在validatefields 进行所有表单组件统一校验时, err打印出来是null, 过了一会后 又会在控制台弹出校验失败, 自定义验证器是异步action


You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub, or unsubscribe.

太坑了

校验函数里面如果出现异常,没有任何提示。
坑了半天。。。

我在想这个AntdV是不维护了么

@kongling94 ?

同样遇到这个问题,没下文了?

<a-input id="code" placeholder="请输入用户ID"v-decorator="[ 'code', {rules: [{ required: true, message: '请输入用户ID' }, { validator: handleUsername }], validateTrigger: 'change'} ]" > </a-input>
handleUsername(rule, value, callback) { value = value.trim() if (value.length == 0) { this.username = ''; callback(); } else { fetchUsername({ code: value }) .then((rest) => { if (rest.code == 200) { this.username = rest.data; //callback(); } else { this.username = ''; callback(new Error(rest.msg)) return; } }).finally(() => { callback() }) } },
<a-form :label-col="labelCol" :wrapper-col="wrapperCol" :form="form" @submit="handleSubmit"> <a-button type="primary" htmlType="submit">确定</a-button> </a-form>
点确定没有任何反应 不进行任何提示,在console 里面输入async-validator:["code is required"]
根本没有进validateFields
去掉 validator 和 validateTrigger 点确定就会正常提示。
这是什么问题?

<a-input id="code" placeholder="请输入用户ID"v-decorator="[ 'code', {rules: [{ required: true, message: '请输入用户ID' }, { validator: handleUsername }], validateTrigger: 'change'} ]" > </a-input>
handleUsername(rule, value, callback) { value = value.trim() if (value.length == 0) { this.username = ''; callback(); } else { fetchUsername({ code: value }) .then((rest) => { if (rest.code == 200) { this.username = rest.data; //callback(); } else { this.username = ''; callback(new Error(rest.msg)) return; } }).finally(() => { callback() }) } },
<a-form :label-col="labelCol" :wrapper-col="wrapperCol" :form="form" @submit="handleSubmit"> <a-button type="primary" htmlType="submit">确定</a-button> </a-form>
点确定没有任何反应 不进行任何提示,在console 里面输入async-validator:["code is required"]
根本没有进validateFields
去掉 validator 和 validateTrigger 点确定就会正常提示。
这是什么问题?

请问你这个问题解决了吗?同样碰到了这个问题

<a-input id="code" placeholder="请输入用户ID"v-decorator="[ 'code', {rules: [{ required: true, message: '请输入用户ID' }, { validator: handleUsername }], validateTrigger: 'change'} ]" > </a-input>
handleUsername(rule, value, callback) { value = value.trim() if (value.length == 0) { this.username = ''; callback(); } else { fetchUsername({ code: value }) .then((rest) => { if (rest.code == 200) { this.username = rest.data; //callback(); } else { this.username = ''; callback(new Error(rest.msg)) return; } }).finally(() => { callback() }) } },
<a-form :label-col="labelCol" :wrapper-col="wrapperCol" :form="form" @submit="handleSubmit"> <a-button type="primary" htmlType="submit">确定</a-button> </a-form>
点确定没有任何反应 不进行任何提示,在console 里面输入async-validator:["code is required"]
根本没有进validateFields
去掉 validator 和 validateTrigger 点确定就会正常提示。
这是什么问题?

请问你这个问题解决了吗?同样碰到了这个问题

// 自定义密码校验
  handleConfirmPassword = (rule, value, callback) => {
    const { getFieldValue } = this.props.form;
    if (value && value !== getFieldValue('newPwd')) {
      callback('两次输入不一致!');
    }

    // Note: 必须总是返回一个 callback,否则 validateFieldsAndScroll 无法响应
    callback();
  };
我是这么解决的

validatorFun(rule, value, callback) {
const psw = this.form.getFieldValue('newPwd')
if (value && value !== psw) {
rule.message = '两次密码不一致'
callback(rule)
}
callback()
}

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Orbyt picture Orbyt  ·  3Comments

alanwei0 picture alanwei0  ·  3Comments

longhuasishen picture longhuasishen  ·  3Comments

plandem picture plandem  ·  3Comments

xtznhzxdev picture xtznhzxdev  ·  3Comments