|
| 1 | +# rc-field-form |
| 2 | + |
| 3 | +React Performance First Form Component. |
| 4 | + |
| 5 | +[![NPM version][npm-image]][npm-url] [](https://github.com/umijs/dumi) [![build status][github-actions-image]][github-actions-url] [![Codecov][codecov-image]][codecov-url] [![npm download][download-image]][download-url] |
| 6 | + |
| 7 | +[npm-image]: http://img.shields.io/npm/v/rc-field-form.svg?style=flat-square |
| 8 | +[npm-url]: http://npmjs.org/package/rc-field-form |
| 9 | +[github-actions-image]: https://github.com/react-component/field-form/workflows/CI/badge.svg |
| 10 | +[github-actions-url]: https://github.com/react-component/field-form/actions |
| 11 | +[codecov-image]: https://img.shields.io/codecov/c/github/react-component/field-form/master.svg?style=flat-square |
| 12 | +[codecov-url]: https://codecov.io/gh/react-component/field-form/branch/master |
| 13 | +[download-image]: https://img.shields.io/npm/dm/rc-field-form.svg?style=flat-square |
| 14 | +[download-url]: https://npmjs.org/package/rc-field-form |
| 15 | + |
| 16 | +## Development |
| 17 | + |
| 18 | +```bash |
| 19 | +npm install |
| 20 | +npm start |
| 21 | +open http://localhost:8000 |
| 22 | +``` |
| 23 | + |
| 24 | +## Feature |
| 25 | + |
| 26 | +- Support react.js and even react-native |
| 27 | +- Validate fields with [async-validator](https://github.com/yiminghe/async-validator/) |
| 28 | + |
| 29 | +## Install |
| 30 | + |
| 31 | +[](https://npmjs.org/package/rc-field-form) |
| 32 | + |
| 33 | +## Usage |
| 34 | + |
| 35 | +```js | pure |
| 36 | +import Form, { Field } from 'rc-field-form'; |
| 37 | + |
| 38 | +<Form |
| 39 | + onFinish={values => { |
| 40 | + console.log('Finish:', values); |
| 41 | + }} |
| 42 | +> |
| 43 | + <Field name="username"> |
| 44 | + <Input placeholder="Username" /> |
| 45 | + </Field> |
| 46 | + <Field name="password"> |
| 47 | + <Input placeholder="Password" /> |
| 48 | + </Field> |
| 49 | + |
| 50 | + <button>Submit</button> |
| 51 | +</Form>; |
| 52 | + |
| 53 | +export default Demo; |
| 54 | +``` |
| 55 | + |
| 56 | +## 🔥 API |
| 57 | + |
| 58 | +We use typescript to create the Type definition. You can view directly in IDE. But you can still check the type definition [here](https://github.com/react-component/field-form/blob/master/src/interface.ts). |
| 59 | + |
| 60 | +### Form |
| 61 | + |
| 62 | +| Prop | Description | Type | Default | |
| 63 | +| ---------------- | -------------------------------------------------- | -------------------------------------------- | ---------------- | |
| 64 | +| component | Customize Form render component | string \| Component \| false | form | |
| 65 | +| fields | Control Form fields status. Only use when in Redux | [FieldData](#fielddata)[] | - | |
| 66 | +| form | Set form instance created by `useForm` | [FormInstance](#useform) | `Form.useForm()` | |
| 67 | +| initialValues | Initial value of Form | Object | - | |
| 68 | +| name | Config name with [FormProvider](#formprovider) | string | - | |
| 69 | +| preserve | Preserve value when field removed | boolean | false | |
| 70 | +| validateMessages | Set validate message template | [ValidateMessages](#validatemessages) | - | |
| 71 | +| onFieldsChange | Trigger when any value of Field changed | (changedFields, allFields) => void | - | |
| 72 | +| onFinish | Trigger when form submit and success | (values) => void | - | |
| 73 | +| onFinishFailed | Trigger when form submit and failed | ({ values, errorFields, outOfDate }) => void | - | |
| 74 | +| onValuesChange | Trigger when any value of Field changed | (changedValues, values) => void | - | |
| 75 | + |
| 76 | +### Field |
| 77 | + |
| 78 | +| Prop | Description | Type | Default | |
| 79 | +| ----------------- | ----------------------------------------------------------------------------- | ------------------------------------------- | -------- | |
| 80 | +| dependencies | Will re-render if dependencies changed | [NamePath](#namepath)[] | - | |
| 81 | +| getValueFromEvent | Specify how to get value from event | (..args: any[]) => any | - | |
| 82 | +| getValueProps | Customize additional props with value. This prop will disable `valuePropName` | (value) => any | - | |
| 83 | +| initialValue | Field initial value | any | - | |
| 84 | +| name | Field name path | [NamePath](#namepath) | - | |
| 85 | +| normalize | Normalize value before update | (value, prevValue, prevValues) => any | - | |
| 86 | +| preserve | Preserve value when field removed | boolean | false | |
| 87 | +| rules | Validate rules | [Rule](#rule)[] | - | |
| 88 | +| shouldUpdate | Check if Field should update | true \| (prevValues, nextValues) => boolean | - | |
| 89 | +| trigger | Collect value update by event trigger | string | onChange | |
| 90 | +| validateTrigger | Config trigger point with rule validate | string \| string[] | onChange | |
| 91 | +| valuePropName | Config value mapping prop with element | string | value | |
| 92 | + |
| 93 | +### List |
| 94 | + |
| 95 | +| Prop | Description | Type | Default | |
| 96 | +| -------- | ------------------------------- | ------------------------------------------------------------------------------------------------------- | ------- | |
| 97 | +| name | List field name path | [NamePath](#namepath)[] | - | |
| 98 | +| children | Render props for listing fields | (fields: { name: [NamePath](#namepath) }[], operations: [ListOperations](#listoperations)) => ReactNode | - | |
| 99 | + |
| 100 | +### useForm |
| 101 | + |
| 102 | +Form component default create an form instance by `Form.useForm`. But you can create it and pass to Form also. This allow you to call some function on the form instance. |
| 103 | + |
| 104 | +```jsx | pure |
| 105 | +const Demo = () => { |
| 106 | + const [form] = Form.useForm(); |
| 107 | + return <Form form={form} />; |
| 108 | +}; |
| 109 | +``` |
| 110 | + |
| 111 | +For class component user, you can use `ref` to get form instance: |
| 112 | + |
| 113 | +```jsx | pure |
| 114 | +class Demo extends React.Component { |
| 115 | + setRef = form => { |
| 116 | + // Form instance here |
| 117 | + }; |
| 118 | + |
| 119 | + render() { |
| 120 | + return <Form ref={this.setRef} />; |
| 121 | + } |
| 122 | +} |
| 123 | +``` |
| 124 | + |
| 125 | +| Prop | Description | Type | |
| 126 | +| ----------------- | ------------------------------------------ | -------------------------------------------------------------------------- | |
| 127 | +| getFieldValue | Get field value by name path | (name: [NamePath](#namepath)) => any | |
| 128 | +| getFieldsValue | Get list of field values by name path list | (nameList?: ([NamePath](#namepath)[]) => any) \| true | |
| 129 | +| getFieldError | Get field errors by name path | (name: [NamePath](#namepath)) => string[] | |
| 130 | +| getFieldsError | Get list of field errors by name path list | (nameList?: [NamePath](#namepath)[]) => FieldError[] | |
| 131 | +| isFieldsTouched | Check if list of fields are touched | (nameList?: [NamePath](#namepath)[], allTouched?: boolean) => boolean | |
| 132 | +| isFieldTouched | Check if a field is touched | (name: [NamePath](#namepath)) => boolean | |
| 133 | +| isFieldValidating | Check if a field is validating | (name: [NamePath](#namepath)) => boolean | |
| 134 | +| resetFields | Reset fields status | (fields?: [NamePath](#namepath)[]) => void | |
| 135 | +| setFields | Set fields status | (fields: FieldData[]) => void | |
| 136 | +| setFieldsValue | Set fields value | (values) => void | |
| 137 | +| submit | Trigger form submit | () => void | |
| 138 | +| validateFields | Trigger fields to validate | (nameList?: [NamePath](#namepath)[], options?: ValidateOptions) => Promise | |
| 139 | + |
| 140 | +### FormProvider |
| 141 | + |
| 142 | +| Prop | Description | Type | Default | |
| 143 | +| ---------------- | ----------------------------------------- | ---------------------------------------- | ------- | |
| 144 | +| validateMessages | Config global `validateMessages` template | [ValidateMessages](#validatemessages) | - | |
| 145 | +| onFormChange | Trigger by named form fields change | (name, { changedFields, forms }) => void | - | |
| 146 | +| onFormFinish | Trigger by named form fields finish | (name, { values, forms }) => void | - | |
| 147 | + |
| 148 | +## 📋 Interface |
| 149 | + |
| 150 | +### NamePath |
| 151 | + |
| 152 | +| Type | |
| 153 | +| ---------------------------------------- | |
| 154 | +| string \| number \| (string \| number)[] | |
| 155 | + |
| 156 | +### FieldData |
| 157 | + |
| 158 | +| Prop | Type | |
| 159 | +| ---------- | ---------------------------------------- | |
| 160 | +| touched | boolean | |
| 161 | +| validating | boolean | |
| 162 | +| errors | string[] | |
| 163 | +| name | string \| number \| (string \| number)[] | |
| 164 | +| value | any | |
| 165 | + |
| 166 | +### Rule |
| 167 | + |
| 168 | +| Prop | Type | |
| 169 | +| --------------- | ----------------------------------------------------------------------------------------------- | |
| 170 | +| enum | any[] | |
| 171 | +| len | number | |
| 172 | +| max | number | |
| 173 | +| message | string | |
| 174 | +| min | number | |
| 175 | +| pattern | RegExp | |
| 176 | +| required | boolean | |
| 177 | +| transform | (value) => any | |
| 178 | +| type | string | |
| 179 | +| validator | ([rule](#rule), value, callback: (error?: string) => void, [form](#useform)) => Promise \| void | |
| 180 | +| whitespace | boolean | |
| 181 | +| validateTrigger | string \| string[] | |
| 182 | + |
| 183 | +#### validator |
| 184 | + |
| 185 | +To keep sync with `rc-form` legacy usage of `validator`, we still provides `callback` to trigger validate finished. But in `rc-field-form`, we strongly recommend to return a Promise instead. |
| 186 | + |
| 187 | +### ListOperations |
| 188 | + |
| 189 | +| Prop | Type | |
| 190 | +| ------ | ------------------------ | |
| 191 | +| add | (initValue: any) => void | |
| 192 | +| remove | (index: number) => void | |
| 193 | + |
| 194 | +### ValidateMessages |
| 195 | + |
| 196 | +Validate Messages provides a list of error template. You can ref [here](https://github.com/react-component/field-form/blob/master/src/utils/messages.ts) for fully default templates. |
| 197 | + |
| 198 | +| Prop | Description | |
| 199 | +| ------- | ------------------- | |
| 200 | +| enum | Rule `enum` prop | |
| 201 | +| len | Rule `len` prop | |
| 202 | +| max | Rule `max` prop | |
| 203 | +| min | Rule `min` prop | |
| 204 | +| name | Field name | |
| 205 | +| pattern | Rule `pattern` prop | |
| 206 | +| type | Rule `type` prop | |
| 207 | + |
| 208 | +## Different with `rc-form` |
| 209 | + |
| 210 | +`rc-field-form` is try to keep sync with `rc-form` in api level, but there still have something to change: |
| 211 | + |
| 212 | +### 1. Field will not keep snyc with `initialValues` when un-touched |
| 213 | + |
| 214 | +In `rc-form`, field value will get from `initialValues` if user not operate on it. |
| 215 | +It's a bug but user use as a feature which makes fixing will be a breaking change and we have to keep it. |
| 216 | +In Field Form, this bug will not exist anymore. If you want to change a field value, use `setFieldsValue` instead. |
| 217 | + |
| 218 | +### 2. Remove Field will not clean up related value |
| 219 | + |
| 220 | +We do lots of logic to clean up the value when Field removed before. But with user feedback, remove exist value increase the additional work to keep value back with conditional field. |
| 221 | + |
| 222 | +### 3. Nest name use array instead of string |
| 223 | + |
| 224 | +In `rc-form`, we support like `user.name` to be a name and convert value to `{ user: { name: 'Bamboo' } }`. This makes '.' always be the route of variable, this makes developer have to do additional work if name is real contains a point like `app.config.start` to be `app_config_start` and parse back to point when submit. |
| 225 | + |
| 226 | +Field Form will only trade `['user', 'name']` to be `{ user: { name: 'Bamboo' } }`, and `user.name` to be `{ ['user.name']: 'Bamboo' }`. |
| 227 | + |
| 228 | +### 4. Remove `validateFieldsAndScroll` |
| 229 | + |
| 230 | +Since `findDomNode` is marked as warning in [StrictMode](https://reactjs.org/docs/strict-mode.html#warning-about-deprecated-finddomnode-usage). It seems over control of Form component. |
| 231 | +We decide to remove `validateFieldsAndScroll` method and you should handle it with you own logic: |
| 232 | + |
| 233 | +```jsx | pure |
| 234 | +<Form> |
| 235 | + <Field name="username"> |
| 236 | + <input ref={this.inputRef} /> |
| 237 | + </Field> |
| 238 | +</Form> |
| 239 | +``` |
| 240 | + |
| 241 | +### 5. `getFieldsError` always return array |
| 242 | + |
| 243 | +`rc-form` returns `null` when no error happen. This makes user have to do some additional code like: |
| 244 | + |
| 245 | +```js | pure |
| 246 | +(form.getFieldsError('fieldName') || []).forEach(() => { |
| 247 | + // Do something... |
| 248 | +}); |
| 249 | +``` |
| 250 | + |
| 251 | +Now `getFieldsError` will return `[]` if no errors. |
| 252 | + |
| 253 | +### 6. Remove `callback` with `validateFields` |
| 254 | + |
| 255 | +Since ES8 is support `async/await`, that's no reason not to use it. Now you can easily handle your validate logic: |
| 256 | + |
| 257 | +```js | pure |
| 258 | +async function() { |
| 259 | + try { |
| 260 | + const values = await form.validateFields(); |
| 261 | + console.log(values); |
| 262 | + } catch (errorList) { |
| 263 | + errorList.forEach(({ name, errors }) => { |
| 264 | + // Do something... |
| 265 | + }); |
| 266 | + } |
| 267 | +} |
| 268 | +``` |
| 269 | + |
| 270 | +**Notice: Now if your validator return an `Error(message)`, not need to get error by `e => e.message`. FieldForm will handle this.** |
| 271 | + |
| 272 | +### 7. `preserve` is default to false |
| 273 | + |
| 274 | +In `rc-form` you should use `preserve` to keep a value cause Form will auto remove a value from Field removed. Field Form will always keep the value in the Form whatever Field removed. But you can still use `preserve=false` to disable value keeping since `1.5.0`. |
| 275 | + |
| 276 | +### 8. `setFields` not trigger `onFieldsChange` and `setFieldsValue` not trigger `onValuesChange` |
| 277 | + |
| 278 | +In `rc-form`, we hope to help user auto trigger change event by setting to make redux dispatch easier, but it's not good design since it makes code logic couping. |
| 279 | + |
| 280 | +Additionally, user control update trigger `onFieldsChange` & `onValuesChange` event has potential dead loop risk. |
0 commit comments