> 最近在搞react + antd,在弄form表单的时候,觉得没写一次都要重新写一次Form, Form.Item,感觉有些麻烦;就想啊,能不能像vue+element那样通过json配置的方式,实现一个form组件
## 1. 假设你已经安装好了依赖,已经可以通过react + antd搭建页面了
> 以下是我的项目主要依赖,版本不同,写法可能也有些不同:
```json
"react": "^18.1.0",
"react-dom": "^18.1.0",
"antd": "^3.26.19",
"react-router": "^3.2.0",
```
## 2.搭建Form组件模子
```js
import React from "react"
import { Form, Col, Row} from "antd"
class Index extends React.Component {
render() {
return (
<Form>
Form>
)
}
}
export default Form.create()(Index)
```
## 3. 老生长谈的,配合Row与Col组件
> 主要是用 Col来做Form.Item的容器
```js
const Item = Form.Item
class Index extends React.Component {
//这个地方就是我们用来遍历父组件传过来的json数组配置,我们就定义这个配置属性是fields
getFormField = () => {
//这里要注意以下 form是通过Form.create()(xxx)
const {form, getFieldDecorator, fields} = this.props
return fields.map((item, index)=>(
<Col key={item.field + item.label + index} >
<Item label={item.label}>
{
getFieldDecorator(item.field, {
//...表单项配置
})(
//表单项配置
)
}
Item>
Col>
))
}
render() {
return (
<Form>
<Row gutter={24}>{this.getFormField()}Row>
Form>
)
}
}
//...省略
```
## 4. fields json数组的格式制定
```js
// {label: "姓名", field: "name", attrs: {rules: [{ required: true, message: "请输入!" }], initialValue: "张三",} component: }
// 那么 getFormField 方法也就可以完善了
getFormField = () => {
//这里要注意以下 form是通过Form.create()(xxx)
const {form, span = 6, fields} = this.props
const {getFieldDecorator} = form
return fields.map((item, index)=>(
<Col span={span} key={item.field + item.label + index} >
<Item label={item.label}>
{
getFieldDecorator(item.field, {
...item.attrs
})(
item.component
)
}
Item>
Col>
))
}
```
## 5. 尝试一下
**父组件**
```js
//导入Form组件
import FormPage from "./components/form"
import {Input, InputNumber} from 'antd'
function App() {
const fields = [
{ label: "姓名", field: "name", attrs: { rules: [{ required: true, message: "请输入!" }] }, component: <Input allowClear placeholder="请输入姓名" /> },
{ label: "年龄", field: "age", attrs: { rules: [{ required: true, message: "请输入!" }] }, component: <InputNumber allowClear placeholder="请输入年龄" style={{width: "100%"}}/> },
]
return (
<div className="App">
<FormPage fields={fields}/>
div>
);
}
```
#### 效果预览
## 6. 那么,怎么在父组件上获取组件上表单项的值呢?
### 6.1 通过ref
```js
// 父组件中创建一个ref,绑定组件
import React from "react"
import FormPage from "./components/form"
function App() {
//创建ref对象
const fromRef = React.createRef()
const handleForm = ()=> {
//使用ref对象
formRef.current.validateFields((err, vals) => {
if(err) return
console.log(vals)
})
}
//...省略
return (
<div className="App">
// ref对象与非状态组件绑定
<FormPage fields={fields} ref={fromRef}/>
<div style={{textAlign: "right"}}>
<Button type="primary" onClick={handleForm}>获取Button>
div>
div>
);
}
```
#### 效果预览
### 6.2 使用 rc-form 提供的 wrappedComponentRef
```js
import React, { useState} from "react"
import FormPage from "./components/form"
function App() {
const EnhancedForm = FormPage
//非状态组件,用hooks定义变量
const [form, setform] = useState();
const handleForm = ()=> {
//使用
form.props.form.validateFields((err, vals) => {
if(err) return
console.log(vals)
})
}
//...省略
return (
<div className="App">
<EnhancedForm wrappedComponentRef={(form) => setform(form)} fields={fields} />
<div style={{textAlign: "right"}}>
<Button type="primary" onClick={handleForm}>获取Button>
div>
div>
);
}
```
#### 效果预览
### 6.3 通过回调结合组件的componentDidMount生命钩子
**这种方法需要我们稍稍改造一下FormPage组件**
```js
//...省略
class Index extends React.Component {
//...省略
getForm = ()=> {
const { form} = this.props
this.props.getForm && this.props.getForm(form)
}
componentDidMount() {
this.getForm()
}
//...省略
}
//...省略
```
**父组件使用**
```js
import React, { useState} from "react"
import FormPage from "./components/form"
function App() {
const [form, setform] = useState();
const handleForm = ()=> {
console.log(form)
//使用
form.validateFields((err, vals) => {
if(err) return
console.log(vals)
})
}
//...省略
return (
<div className="App">
<FormPage fields={fields} getForm={(f) => setform(f)}/>
<div style={{textAlign: "right"}}>
<Button type="primary" onClick={handleForm}>获取Button>
div>
div>
);
}
```
#### 效果预览