antd快速開發(Form篇) 前言 由於一直在做中台業務,後臺項目特別多,但是後臺項目的特點是:大量的列表和大量表單,重覆開發會降低效率,所以我這邊總結了一下使用 組件搭建 的快捷方法。希望能對大家有用。 傳統Form搭建 首先傳統搭建一個form表單,那麼代碼可能會是下麵這樣子 目前只有兩個表單 ...
antd快速開發(Form篇)
前言
由於一直在做中台業務,後臺項目特別多,但是後臺項目的特點是:大量的列表和大量表單,重覆開發會降低效率,所以我這邊總結了一下使用antd
組件搭建form
的快捷方法。希望能對大家有用。
傳統Form搭建
首先傳統搭建一個form表單,那麼代碼可能會是下麵這樣子
import React from 'react';
import { Form, Input } from 'antd';
@Form.create()
class MyTestForm extends React.Component {
render() {
const { form: { getFieldDecorator } } = this.props;
return(
<Form>
<Form.Item label='姓名'>
{
getFieldDecorator('username', {
rules: [
{
required: true,
message: '這是必填項'
}
]
})(<Input placeholder="placeholder" />)
}
</Form.Item>
<Form.Item label='密碼'>
{
getFieldDecorator('password', {
rules: [
{
required: true,
message: '這是必填項'
}
]
})(<Input placeholder="placeholder" />)
}
</Form.Item>
</Form>
)
}
}
export default MyTestForm;
目前只有兩個表單項,看起來代碼還挺清晰的,假如這個表單是很複雜的表單,有多個表單項,這塊的代碼會很長,維護和開發起來都是不方便,最重要的再來一個大的表單,你還是會需要寫這麼多的代碼。這樣就影響了開效率。
優化後的Form
我們想要的是,儘量少寫(不寫)重覆性的代碼,讓代碼的復用性更高。我這塊做了一些優化,主要的流程如圖:
主要的幾個點:
- 將最底層,最重要的部分抽離出來,也就是
BaseItem
組件,BaseItem
具備的能力是form的能力,雙向綁定的能力等,純凈的組件,不包含任何UI層面的東西。 - UI層的東西也單獨抽離出來,以方便以後UI層面的拓展。(例如想自己設計一套UI樣式,就只用直接新開發一套UI層面的東西,而不用但心原子層組件的代碼)。
- 每個表單的信息抽離成配置文件,使頁面維護起來更方便。
具體的代碼如下
//BaseItem.js(原子層)
const BaseItem = (props) => {
const { form: { getFieldDecorator }, config } = props;
const { name, children, ...argv } = config;
return name ? getFieldDecorator(name, { ...argv })(children) : (children);
}
export default BaseItem;
//ItemLayout.js(UI組件)
import React from 'react';
import { Form } from 'antd';
//Layout也可以用自己的UI組件
const Layout = ({ config: { itemOptions }, children }) =>
<Form.Item { ...itemOptions } >{ children }</Form.Item>
const hidden = (isHidden) => {
const type = typeof(isHidden);
return (type === 'function' && isHidden()) || type === undefined || isHidden;
//預設是顯示
}
class ItemLayout extends React.Component {
render() {
const { children } = this.props;
return(
<>
{
React.Children.map(
children, (child, i) => {
const { config: { isHidden, ...argv}} = child.props;
return hidden(isHidden) ? null : //具有隱藏表單項能力
React.cloneElement(
<Layout { ...argv }>{ child }</Layout>,
{
...children.props
}
)
}
)
}
</>
)
}
}
export default ItemLayout;
//config.js(配置文件)
import React from 'react';
import { Input } from 'antd';
export const formConfig = () => {
return [
{
itemOptions : { //Form.Item的api配置
label: '姓名'
//...argv
},
name: 'username',
initialValue: '',
rules: [],
children: <Input />
//...argv
},
{
itemOptions : {
label: '密碼'
},
name: 'password',
initialValue: '',
rules: [],
children: <Input />,
isHidden: true //隱藏此項 預設是顯示
},
{
itemOptions : {
label: '密碼'
},
name: 'password',
initialValue: '',
rules: [],
children: <Input />,
isHidden: () => false //通過方法來動態顯示隱藏
},
]
}
支持
antd Form
所有的api
。配置文件為什麼是使用函數的形式?因為可以通過函數的參數,實現配置文件和頁面之間進行數據的傳遞。
在頁面就這樣使用,代碼如下
import React from 'react';
import { Form, Input } from 'antd';
import { formConfig } from './config.js';
import BaseItem from './BaseItem';
import ItemLayout from './ItemLayout';
@Form.create()
class MyTestForm extends React.Component {
render() {
const { form } = this.props;
return(
<Form>
{
formConfig().map((item, i) =>
<ItemLayout><BaseItem key={i} config={item} form={form}/></ItemLayout>)
}
</Form>
)
}
}
export default MyTestForm;
相比傳統搭建Form是不是快捷了很多,而且頁面代碼層面更顯得更清晰。
註意:
假如是想使用自定義的組件,(一個個性化的業務組件),簡單點,我對Input的封裝
//自動trim的Input
import { Component } from 'react';
import { Input } from 'antd';
class TrimInput extends Component {
handleChange = (e) => {
e.target.value = e.target.value.trim();
this.props.onChange(e.target.value); //Input Chang 後將值傳遞給props
}
render() {
const { value, ...argv } = this.props;
return(
<Input
value={ value } //將props的填在Input中
{ ...argv }
onChange={this.handleChange}/>
)
}
}
export default TrimInput;
自定義的業務組件需要具備雙向數據流的能力,最重要的一點是在更新的時候需要 調用
this.props.onChange(data)
。
搜索Form包裝(SearchForm
)
假如覺得這還不夠過癮,那麼一起來基於BaseItem
來再包裝業務組件吧。相信每個後臺都有搜索能力吧,那麼我們就包裝一個搜索的SearchForm
。
主要就是增加一個search
功能並把form的值傳遞出去。
主要代碼如下:
//searchForm.js
import React from 'react';
import { Form, Button } from 'antd';
import BaseItem from './BaseItem';
import ItemLayout from './ItemLayout';
@Form.create()
class SearchForm extends React.Component {
handleSearch = () => {
const { form: { validateFields }, search } = this.props;
validateFields((err, fieldsValue) => {
console.log(fieldsValue);
if(!err) {
search && search(fieldsValue);
}
})
}
render() {
const { form, searchConfig, search, form: { resetFields } } = this.props;
return(
<>
<Form onSubmit={this.handleSearch}>
{
searchConfig().map((item, i) =>
<ItemLayout><BaseItem key={i} config={item} form={form}/></ItemLayout>)
}
{
search && <div>
<Button htmlType="submit" type="primary" style={{marginRight: '20px'}}>
搜索
</Button>
<Button onClick={() => resetFields()}>重置</Button>
</div>
}
</Form>
</>
)
}
}
export default SearchForm;
頁面裡面使用,表單項還是抽成配置文件使用:
//config.js 查詢條件
export const searchConfig = () => {
return [
{
itemOptions : {
label: '條件一'
},
name: 'name1',
initialValue: '',
rules: [],
children: <Input />
},
{
itemOptions : {
label: '條件二'
},
name: 'name2',
initialValue: '',
rules: [],
children: <Input />
}
]
}
import React from 'react';
import { searchConfig } from './config';
import SearchForm from './SearchForm';
class MyTestForm extends React.Component {
handleSearch = value => {
console.log(value);//獲取到的查詢條件
}
render() {
return(
<SearchForm searchConfig={searchConfig} search={this.handleSearch} />
)
}
}
export default MyTestForm;
這樣子寫查詢表單是不是很快呀,以後遇到查詢就引用這個組件,然後抽一個配置文件,這樣就OK了。
Modal + Form
還有也會經常遇到這種情況,彈窗裡面的Form
,這樣子就需要給彈窗增加收集數據的能力。相當於我們把searchForm
的組件放在 Modal 裡面。 具體實現代碼就不貼了。
antd Form
需要註意的幾個問題。
initialValue
這個屬性只是設置表單的初始值,當需要動態更改表單的值的時候,使用setFieldsValue
resetFields
這個屬性是重置一組輸入控制項的值與狀態,(將值重置為initialValue
, 而不是清空數據,需要清空數據還是使用setFieldsValue
)
antd Form
新的改動
antd Form
將在第4個版本使用 rc-field-form
, 但是還沒有發佈,我是在4.0-prepare分支上看到。
那麼兩個底層組件 有什麼區別呢?
首先rc-field-form
會儘量在api
層面上保持一致,但是仍有地方做了改動。 主要是以下幾點:
當沒有手動更新過表單的時候,將不會收集
initialValues
的值在
rc-form
裡面,如果用戶沒有操作過表單,將會從form的initialValues
收集值。他們認為這是一個bug,但是好多用戶是用了這個,所以他們不做修複。在rc-field-form
中,將不會有這個bug。如果想改變組件的值,使用setFieldsValue
代替。嵌套的name使用數組代替字元串
rc-form
裡面支持user.name
,最終會被解釋成為{user:{ name: '' } }
rc-field-form
將是['user', 'name']
解釋成為{user: { name: '' }}
並且會把user.name
解釋成為{ ['user.name']: ''}
刪除
validateFieldsAndScroll
這個屬性是因為使用了
findDomNode
,但是findDomNode
在StrictMode
中被標記為警告。認為這是對錶單組件的過度控制。getFieldsError
將總是返回來數組rc-form
當沒有錯的時候,返回的是null,rc-field-form
現在返回的是一個空數組刪除了
validateFields
的callback函數是因為
ES8
支持async/await
,沒有理由不使用它。我們使用的時候應該是async function() { try { const values = await form.validateFields(); console.log(values); } catch (errorList) { errorList.forEach(({ name, errors }) => { // Do something... }); } }
setFields
將不觸發onFieldsChange
和setFieldsValue
不觸發onValuesChange
總結
寫這篇文章主要是自己做後臺最Form的總結,還有是為大家提供一種思路,後臺快速開發的方式。
後面還會更新其它antd 組件,主要是如何開發更適合業務場景的組件。