本篇文章主要介绍react+hooks实现表单提交的基础案例,其中依赖于ant-design4.0版本的UI组件。其中分为两种表单提交场景
首先讲解下使用Form表单的一些注意点:
如何获取表单实例?
使用Form组件中自带的hooks可以获取到表单实例 const [form] = Form.useForm()
将form组件挂在到Form组件上 <Form form={form}></Form>
此时form变量就是Form表单的实例,可以调用实例上的方法直接操作表单
表单字段如何自定义校验规则?
在Form.Item组件中的rules属性中可以添加validator属性,该属性值为一个函数,可接收三个参数:rule ,
value, callback。验证不通过在callback中传入错误原因即可,验证通过直接调用callback就行
防止表单重复提交 ?
提交时调用提交接口的那个函数一定要用useCallback hooks, useCallback返回一个函数,此场景中主要是为了实时监听flag(提交请求是否完成)值的变化来判断是否可以发起请求
表单提交示例代码
-
表单内提交:即提交按钮在form组件内, 注意点:
提交按钮必须在Form表单内,且Button组件要添加htmlType="submit",这样点击按钮才会自动提交表单
import React, { useState, useCallback} from 'react'
import { Form, Input, Radio, Select, Button,message} from 'antd'
import {register} from '../../api/formCase'
import styles from './index.module.scss'
const {Option} = Select
const formItemLayout = {
labelCol: { span: 6 },
wrapperCol: { span: 10 },
}
let FormCase:any = ()=> {
const [flag, setFlag] = useState<boolean>(false)
const [form] = Form.useForm()
const validatePass = (rule:any, value:any, callback:any)=> {
const reg = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[^]{8,16}$/
if (value) {
if(reg.test(value)) {
callback()
} else {
callback('至少1个大写字母,1个小写字母和1个数字')
}
} else {
callback('请输入密码')
}
}
const validatePhone = (rule:any, value:any, callback:any)=> {
const reg = /^[1][3,4,5,7,8,9][0-9]{9}$/
console.log(rule, value)
if (value) {
if(reg.test(value)) {
callback()
} else {
callback('手机号格式不正确')
}
} else {
callback('请输入手机号')
}
}
const onFinish = useCallback((values) => {
if(flag) return
setFlag(true)
register(values).then(()=> {
message.success('注册成功');
}).finally(() => {
setFlag(false)
})
},[flag])
const onReset = () => {
form.resetFields()
}
return (
<Form {...formItemLayout} onFinish={onFinish} initialValues={{sex: 1}} form={form}>
<Form.Item name="name" label="用户名:" rules={[{required:true, message:'请输入用户名'}]}>
<Input maxLength={10} />
</Form.Item>
<Form.Item name="password" label="密码:"rules={[{required:true, validator:validatePass}]}>
<Input minLength={8} maxLength={20} />
</Form.Item>
<Form.Item name="phone" label="手机号:" rules={[{required: true, validator: validatePhone}]}>
<Input />
</Form.Item>
<Form.Item name="sex" label="性别:">
<Radio.Group>
<Radio value={1}>男</Radio>
<Radio value={0}>女</Radio>
</Radio.Group>
</Form.Item>
<Form.Item name="country" label="性别:">
<Select placeholder="请选择国家">
<Option value="china">中国</Option>
<Option value="usa">美国</Option>
</Select>
</Form.Item>
{
<Form.Item wrapperCol={{ span: 10, offset: 6 }}>
<div className={styles.btn_wrap}>
<Button className={styles.btn} type="primary" htmlType="submit">
form内提交表单
</Button>
<Button className={styles.btn} onClick={onReset}>
重置
</Button>
</div>
</Form.Item>
}
</Form>
)
}
export default FormCase
-
表单外提交:即表单提交按钮与表单不在同一个组件中(此处按钮在父组件,表单为子组件为例)
注意点: 此种场景需要父组件具有调用子组件方法的能力,需要使用useImperativeHandle, forwardRef 这两个hooks将子组件的方法暴露给父组件
import React, { useState, useRef, useImperativeHandle,forwardRef, useCallback} from 'react'
import { Form, Input, Radio, Select, Button,Divider,message, Col } from 'antd'
import {register} from '../../api/formCase'
import styles from './index.module.scss'
const {Option} = Select
const formItemLayout = {
labelCol: { span: 6 },
wrapperCol: { span: 10 },
}
let FormContent:any = (props:any, ref:any)=> {
const [flag, setFlag] = useState<boolean>(false)
const [form] = Form.useForm()
const validatePass = (rule:any, value:any, callback:any)=> {
const reg = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[^]{8,16}$/
if (value) {
if(reg.test(value)) {
callback()
} else {
callback('至少1个大写字母,1个小写字母和1个数字')
}
} else {
callback('请输入密码')
}
}
const validatePhone = (rule:any, value:any, callback:any)=> {
const reg = /^[1][3,4,5,7,8,9][0-9]{9}$/
console.log(rule, value)
if (value) {
if(reg.test(value)) {
callback()
} else {
callback('手机号格式不正确')
}
} else {
callback('请输入手机号')
}
}
const onFinish = useCallback((values) => {
if(flag) return
setFlag(true)
register(values).then(()=> {
message.success('注册成功');
}).finally(() => {
setFlag(false)
})
},[flag])
useImperativeHandle(ref, () => ({
submitForm: () => {
form.submit()
},
resetForm: () => {
form.resetFields()
}
}));
return (
<Form {...formItemLayout} onFinish={onFinish} initialValues={{sex: 1}} form={form}>
<Form.Item name="name" label="用户名:" rules={[{required:true, message:'请输入用户名'}]}>
<Input maxLength={10} />
</Form.Item>
<Form.Item name="password" label="密码:"rules={[{required:true, validator:validatePass}]}>
<Input minLength={8} maxLength={20} />
</Form.Item>
<Form.Item name="phone" label="手机号:" rules={[{required: true, validator: validatePhone}]}>
<Input />
</Form.Item>
<Form.Item name="sex" label="性别:">
<Radio.Group>
<Radio value={1}>男</Radio>
<Radio value={0}>女</Radio>
</Radio.Group>
</Form.Item>
<Form.Item name="country" label="性别:">
<Select placeholder="请选择国家">
<Option value="china">中国</Option>
<Option value="usa">美国</Option>
</Select>
</Form.Item>
</Form>
)
}
FormContent = forwardRef(FormContent)
function FormCase() {
const formRef:any = useRef()
const formSubmit = () => {
formRef.current.submitForm()
}
const formReset = () => {
formRef.current.resetForm()
}
return(
<div>
<FormContent ref={formRef} />
<Divider />
<Col span={24} offset={6}>
<div className={styles.btn_wrap}>
<Button className={styles.btn} type="primary" onClick={formSubmit}>form外提交表单</Button>
<Button className={styles.btn} onClick={formReset}>重置</Button>
</div>
</Col>
</div>
)
}
export default FormCase