ProForm adds some syntactic sugar and more layout settings to the original Form to help us develop a form quickly. Also add some default behaviors to make our forms work well by default.
Step-by-step forms, Modal forms, Drawer forms, Query forms, lightweight filters, and other layouts cover most scenarios and help you accomplish more with less layout code.
If you want to set default values, please use initialValues, any direct use of component value and onChange may cause value binding failure.
If you want to link forms or do some dependencies, you can use render props mode, ProFormDependency is definitely the best choice
ProForm's onFinish, unlike antd's Form, is a Promise that will automatically set the button to load for you if you return normally.
If you want to listen to a value, it is recommended to use onValuesChange. Keeping a unidirectional data flow is a great benefit for both developers and maintainers
ProForm is not magic: it is just a thin wrapper around antd's Form—use Form.Item for custom controls and mix freely.
ProForm is the best choice when you want to implement a form quickly but don't want to spend too much time on layout.
ProForm is a degradable encapsulation based on antd Form, fully aligned with antd functionality, but adds some preset behaviors and multiple layouts on top of it. These layouts can be switched seamlessly and share a common API.
Many times there is no exact match between the data required by the component and the data required by the backend, and ProForm provides two APIs to solve this problem, transform and convertValue.
convertValue occurs before the component obtains data, usually the data directly sent from the backend to the frontend, and sometimes needs to be refined.
export type SearchConvertKeyFn = (value: any, field: NamePath)=>string | boolean | Record<string, any>; /** * @name Converts the value when getting it, generally used to format the data into the format received by the component * @param value field value * @param namePath field name * @returns the new value of the field * * @example a,b => [a,b] * convertValue:(value,namePath)=>value.split(",") * @example string =>json * convertValue:(value,namePath)=>JSON.parse(value) * @example number =>date * convertValue:(value,namePath)=>Moment(value) * @example YYYY-MM-DD => date * convertValue:(value,namePath)=>Moment(value,"YYYY-MM-DD") * @example string => object * convertValue:(value,namePath)=>({value,label:value}) */ convertValue?: SearchConvertKeyFn;
Transform occurs when submitting, generally speaking, it is spit out the data stored in the database to the backend.
For the convenience of everyone, both ProFormDependency and formRef support transform, which can get the transformed value.
<ProFormDependency> {(value, form) => { // value is transformed by transform // form's current formRef, you can get the unconverted value return ReactNode; }}</ProFormDependency>
formRef has several built-in methods to get the converted value, which is also more functional than antd's Form. For details, see the type definition of ProFormInstance.
/** Get all data formatted by ProForm */ getFieldsFormatValue?: (nameList?: true) => T; /** Get the single data after formatting */ getFieldFormatValue?: (nameList?: NamePath) => T; /** Get the single data after formatting */ getFieldFormatValueObject?: (nameList?: NamePath) => T; /** After validating the fields, return all the data after formatting */ validateFieldsReturnFormatValue?: (nameList?: NamePath[]) => Promise<T>;
export type SearchTransformKeyFn = ( value: any, namePath: string[], allValues: any, ) => string | Record<string, any>; /** * @name Convert value when submitting, generally used to convert value into submitted data * @param value field value * @param namePath field name * @param allValues all fields * The new value of the @returns field, if an object is returned, it will be merged with all values once * * @example {name:[a,b] => {name:a,b } * transform: (value,namePath,allValues)=> value.join(",") * @example {name: string => { newName:string } * transform: (value,namePath,allValues)=> { newName:value } * @example {name:moment} => {name:string * transform: (value,namePath,allValues)=> value.format("YYYY-MM-DD") * @example {name:moment}=> {name:timestamp} * transform: (value,namePath,allValues)=> value.valueOf() * @example {name:{value,label}} => { name:string} * transform: (value,namePath,allValues)=> value.value * @example {name:{value,label}} => { valueName,labelName } * transform: (value)=>{valueName:value.value,labelName:value.name} */ transform?: SearchTransformKeyFn;
#Two common return patterns for transform (recommended)
In practice, transform is usually used in two ways:
1) Return a primitive (most straightforward and stable): it replaces the submitted value of the current field.
2) Return an object (rename/split/write back nested paths): we recommend building the object by the field name/namePath to avoid “looks correct but submit unchanged”.
import { set } from '@rc-component/util';// Write back to the same nested path (recommended)<ProFormText name={['company', 'name']} transform={(value) => set({}, ['company', 'name'], `${value}:suffix`)}/>// Submit: { company: { name: 'xxx:suffix' } }// Rename key example: name -> displayName<ProFormText name="name" transform={(value) => ({ displayName: value })}/>// Submit: { displayName: 'xxx' } (note: this changes the output shape)
Note: SearchTransformKeyFn is typed as (value, namePath: string[], allValues) => any, but in some scenarios (nested paths / ProFormList) the runtime value may not always be the “full path array” you expected. That’s why using the component name to build the return object is often safer.
Except for fixed layout form styles like LightFilter and QueryFilter, other form layouts support configuring three layout methods consistent with antd.
FormItemRender is used specifically to handle code organization using render props, to better aggregate business code with requests, and also to better complete the function of custom form items.
Forms are essential in middle and backend projects, and are usually accompanied by some non-standard controls and complex form items. At this time, custom form items are needed, and completing a custom form item requires at least the implementation of value and onChange. And if the component is only used once and requires a lot of context parameters, then encapsulation is very ungrateful, so there is this component.
Use useControlModel to quickly create a custom form item, while supporting single instance or multiple instances (suitable for encapsulating custom form components and using them in multiple places)
Use withFormItemRender to generate a FormItemRender, which can organize code inline (suitable for scenarios where it is used only once or requires many context parameters)
Use FormControlRender to convert a form component into a render props form, which is useful in certain situations (such as some design defects of certain third-party components, where the component in render cannot call the onChange method, wrapping it at this time can solve it)
Of course, you don't necessarily have to use withFormItemRender. Form.Item can be nested, or Form.Item can be nested with noStyle set on the outer layer to organize your code. This will wrap more div elements. If it does not affect your style, you can also use it.
Using FormControlRender allows you to write code inline and write logic more flexibly. It is suitable for some components wrapped with ProForm.Item or Form.Item.
Sometimes you need to use Form.Item.useStatus, but you must meet the usage specifications of hooks, which makes development require extraction into a separate component for use, and cannot be used inline, and FormControlRender solves this situation very well.
ProForm is a repackaging of antd Form, if you want to customize form elements, ProForm is the same way as antd, you can still customize it with FormItem + custom components. Of course this does not affect other components, QueryFilter and other components as well.
antd's Form api View here, initialValues related knowledge view here
Parameters
Description
Type
Default
onFinish
Callback event after form is submitted and data validation is successful, supports Promise, will automatically set button loading effect
ProFormInstance adds some capabilities compared to antd's form.
/** * Get all data formatted by ProForm * @param nameList boolean * @param omitNil boolean whether to ignore null and undefined * @returns T * * @example getFieldsFormatValue() -> returns all data * @example getFieldsFormatValue(true) -> returns all data, even if not hosted by form * @example getFieldsFormatValue(true, true) -> returns all data, ignoring null and undefined */ getFieldsFormatValue?: (nameList?: true, omitNil?: boolean) => T; /** * Get a single data formatted by ProForm * @param nameList (string|number)[] * @returns T * @example {a:{b:value}} -> getFieldFormatValue(['a', 'b']) -> value */ /** Get the single data after formatting */ getFieldFormatValue?: (nameList?: NamePath) => T; /** * Get the single data formatted by ProForm, including his name * @param nameList (string|number)[] * @returns T * @example * {a:{b:value}} -> getFieldFormatValueObject(['a', 'b']) -> {a:{b:value}} */ getFieldFormatValueObject?: (nameList?: NamePath) => T; /** * Return all data after formatting after field validation * @param nameList (string|number)[] * @returns T * * @example validateFieldsReturnFormatValue -> {a:{b:value}} */ validateFieldsReturnFormatValue?: (nameList?: NamePath[]) => Promise<T>;
While we would prefer not to modify the submitter, it is a common requirement to do so in use, and ProForm's components use the same API to support the requirement.
Parameters
Description
Type
Default
onSubmit
Submit method
(value?: T)=>void
-
onReset
Reset method
(value?: T)=>void
-
searchConfig
The configuration of the search, generally used to configure the text
This property is a high-level wrapper made by ProForm based on the original Antd's FormInstance, adding some more convenient methods. Usage is as follows:
import type { ProFormInstance } from '@ant-design/pro-components';import { ProForm, ProFormDatePicker, ProFormText,} from '@ant-design/pro-components';import { Button, message, Space } from 'antd';import moment from 'dayjs';import { useRef } from 'react';const waitTime = (time: number = 100) => { return new Promise((resolve) => { setTimeout(() => { resolve(true); }, time); });};export default () => { const formRef = useRef<ProFormInstance>(); const onFill = () => { formRef?.current?.setFieldsValue({ name: 'Zhang San', company: 'Ant Financial', }); }; const getCompanyName = () => { message.info( `Company name is "${formRef?.current?.getFieldValue('company')}"`, ); }; const getFormatValues = () => { console.log( 'All data after formatting:', formRef.current?.getFieldsFormatValue?.(), ); }; const validateAndGetFormatValue = async () => { const values = await formRef.current?.validateFieldsReturnFormatValue?.(); console.log('Validate form and return all formatted data:', values); }; return ( <ProForm title="Create Form" formRef={formRef} submitter={{ render: (props, doms) => { return [ ...doms, <Button htmlType="button" onClick={onFill} key="edit"> One-click Fill </Button>, <Button htmlType="button" onClick={getCompanyName} key="read"> Read Company </Button>, <Space.Compact key="refs" style={{ display: 'block' }}> <Button htmlType="button" onClick={getFormatValues} key="format"> Get all formatted data </Button> <Button htmlType="button" onClick={validateAndGetFormatValue} key="format2" > Validate form and return all formatted data </Button> </Space.Compact>, ]; }, }} onFinish={async (values) => { await waitTime(2000); console.log(values); message.success('Submission successful'); return true; }} > <ProFormText width="md" name="name" label="Contract Customer Name" tooltip="Max 24 characters" placeholder="Please enter name" /> <ProFormText width="md" name="company" label="Our Company Name" placeholder="Please enter name" /> <ProFormDatePicker name="date" initialValue={moment('2021-08-09')} /> </ProForm> );};
ProFormInstance adds the following methods based on FormInstance:
Method Name
Usage Description
Remarks
getFieldsFormatValue
Usage is the same as FormInstance's getFieldsValue method, returns all formatted data
getFieldFormatValue
Usage is the same as FormInstance's getFieldValue method, returns formatted specified data
getFieldFormatValueObject
Usage is the same as FormInstance's getFieldValue, returns formatted specified data (including name path)
validateFieldsReturnFormatValue
Usage is the same as FormInstance's validateFields method, returns all formatted data after validation passes