field-form
📝 Performance-first React form state manager with field subscriptions, validation, lists, dependencies, and hook APIs.
Top Related Projects
Build forms in React, without the tears 😭
📋 React Hooks for form state management and validation (Web + React Native)
🏁 High performance subscription-based form state management for React
A Higher Order Component using react-redux to keep form state in a Redux store
Performance-focused API for React forms 🚀
Quick Overview
React-component/field-form is a high-performance React form library that provides a flexible and efficient way to handle form state and validation. It offers a declarative API for building complex forms with ease, supporting both synchronous and asynchronous validation.
Pros
- High performance due to field-level updates
- Flexible API that supports complex form scenarios
- Built-in support for nested fields and arrays
- Seamless integration with React components
Cons
- Steeper learning curve compared to simpler form libraries
- Documentation could be more comprehensive
- Limited built-in UI components
- May be overkill for very simple forms
Code Examples
- Basic form usage:
import { Form, Field } from 'rc-field-form';
const MyForm = () => (
<Form onFinish={values => console.log(values)}>
<Field name="username" rules={[{ required: true }]}>
<input />
</Field>
<button type="submit">Submit</button>
</Form>
);
- Custom field component:
const CustomInput = ({ value, onChange }) => (
<input value={value} onChange={e => onChange(e.target.value)} />
);
const MyForm = () => (
<Form>
<Field name="custom" rules={[{ required: true }]}>
<CustomInput />
</Field>
</Form>
);
- Nested fields:
const NestedForm = () => (
<Form>
<Field name={['user', 'name']} rules={[{ required: true }]}>
<input />
</Field>
<Field name={['user', 'email']} rules={[{ type: 'email' }]}>
<input />
</Field>
</Form>
);
Getting Started
To use react-component/field-form in your project:
-
Install the package:
npm install rc-field-form -
Import and use in your React component:
import { Form, Field } from 'rc-field-form'; const MyForm = () => ( <Form onFinish={values => console.log(values)}> <Field name="username" rules={[{ required: true }]}> <input /> </Field> <button type="submit">Submit</button> </Form> ); -
Customize as needed, adding more fields, validation rules, and form logic.
Competitor Comparisons
Build forms in React, without the tears 😭
Pros of Formik
- More comprehensive documentation and larger community support
- Built-in Yup integration for schema validation
- Simpler API with less boilerplate code for basic form handling
Cons of Formik
- Larger bundle size, which may impact performance for smaller projects
- Less flexible for complex, nested form structures
- Steeper learning curve for advanced use cases
Code Comparison
Field-form:
import { Form, Field } from 'rc-field-form';
<Form onFinish={onFinish}>
<Field name="username">
<Input />
</Field>
<button>Submit</button>
</Form>
Formik:
import { Formik, Form, Field } from 'formik';
<Formik initialValues={{ username: '' }} onSubmit={onSubmit}>
<Form>
<Field name="username" as={Input} />
<button type="submit">Submit</button>
</Form>
</Formik>
Both libraries provide similar functionality for handling form state and validation in React applications. Field-form offers a more lightweight solution with greater flexibility for complex form structures, while Formik provides a more opinionated approach with built-in features like Yup integration and a simpler API for basic use cases. The choice between the two depends on project requirements, performance considerations, and developer preferences.
📋 React Hooks for form state management and validation (Web + React Native)
Pros of react-hook-form
- Lightweight and performant, with minimal re-renders
- Easy to use with a simple API and intuitive hooks
- Excellent TypeScript support and type inference
Cons of react-hook-form
- Less opinionated, which may require more setup for complex forms
- Not as tightly integrated with UI libraries like Ant Design
Code Comparison
react-hook-form:
import { useForm } from "react-hook-form";
function App() {
const { register, handleSubmit } = useForm();
const onSubmit = data => console.log(data);
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input {...register("firstName")} />
<input {...register("lastName")} />
<input type="submit" />
</form>
);
}
field-form:
import { Form, Field } from 'rc-field-form';
function App() {
const [form] = Form.useForm();
return (
<Form form={form} onFinish={console.log}>
<Field name="firstName">
<input />
</Field>
<Field name="lastName">
<input />
</Field>
<button type="submit">Submit</button>
</Form>
);
}
The code comparison shows that react-hook-form has a more concise syntax, while field-form provides a more structured approach with explicit Field components. react-hook-form uses the register function to bind inputs, whereas field-form wraps inputs with Field components. Both libraries offer form state management and submission handling, but with different APIs and levels of abstraction.
🏁 High performance subscription-based form state management for React
Pros of react-final-form
- More lightweight and flexible, with a smaller bundle size
- Better performance due to minimal re-renders
- Extensive documentation and active community support
Cons of react-final-form
- Steeper learning curve for beginners
- Less opinionated, requiring more setup for complex forms
Code Comparison
field-form:
import { Form, Field } from 'rc-field-form';
<Form onFinish={onFinish}>
<Field name="username" rules={[{ required: true }]}>
<Input />
</Field>
<button type="submit">Submit</button>
</Form>
react-final-form:
import { Form, Field } from 'react-final-form';
<Form onSubmit={onSubmit}>
{({ handleSubmit }) => (
<form onSubmit={handleSubmit}>
<Field name="username" validate={required}>
{({ input, meta }) => (
<input {...input} type="text" placeholder="Username" />
)}
</Field>
<button type="submit">Submit</button>
</form>
)}
</Form>
Both libraries offer robust form handling capabilities, but react-final-form provides more flexibility and performance optimizations at the cost of a steeper learning curve. field-form, on the other hand, offers a simpler API that may be more suitable for less complex form requirements or for developers who prefer a more opinionated approach.
A Higher Order Component using react-redux to keep form state in a Redux store
Pros of redux-form
- Deeply integrated with Redux, providing a centralized state management solution
- Extensive documentation and large community support
- Offers powerful features like field-level validation and asynchronous submission
Cons of redux-form
- Adds complexity to the application, especially for simpler forms
- Performance can be impacted due to frequent re-renders
- Requires Redux as a dependency, which may not be necessary for all projects
Code Comparison
redux-form:
import { reduxForm, Field } from 'redux-form';
const MyForm = ({ handleSubmit }) => (
<form onSubmit={handleSubmit}>
<Field name="username" component="input" type="text" />
<button type="submit">Submit</button>
</form>
);
export default reduxForm({ form: 'myForm' })(MyForm);
field-form:
import { Form, Field } from 'rc-field-form';
const MyForm = () => (
<Form onFinish={values => console.log(values)}>
<Field name="username">
<input />
</Field>
<button type="submit">Submit</button>
</Form>
);
export default MyForm;
field-form is a lightweight alternative to redux-form, offering a simpler API and better performance for React applications. It doesn't require Redux and is more flexible in terms of integration with different state management solutions. However, it may lack some advanced features and the extensive ecosystem that redux-form provides.
Performance-focused API for React forms 🚀
Pros of Unform
- More lightweight and performant, with a smaller bundle size
- Offers a simpler API and easier integration with React projects
- Provides better TypeScript support out of the box
Cons of Unform
- Less mature and has a smaller community compared to Field Form
- Fewer built-in form components and validation options
- May require more custom implementation for complex form scenarios
Code Comparison
Unform:
import { Form } from '@unform/web';
import Input from './components/Input';
function MyForm() {
const handleSubmit = (data) => {
console.log(data);
};
return (
<Form onSubmit={handleSubmit}>
<Input name="email" />
<button type="submit">Submit</button>
</Form>
);
}
Field Form:
import { Form, Field } from 'rc-field-form';
function MyForm() {
const [form] = Form.useForm();
return (
<Form form={form} onFinish={(values) => console.log(values)}>
<Field name="email">
<input />
</Field>
<button type="submit">Submit</button>
</Form>
);
}
Both libraries offer form management solutions for React applications, but Unform focuses on simplicity and performance, while Field Form provides more built-in features and a more established ecosystem. The choice between them depends on project requirements and developer preferences.
Convert
designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual CopilotREADME
@rc-component/form
Part of the Ant Design ecosystem.
ð Performance-first React form state manager with field subscriptions, validation, lists, dependencies, and hook APIs.
English | ç®ä½ä¸æ
Highlights
| Area | Support |
|---|---|
| State | Field-level subscriptions, controlled fields, initial values |
| Validation | Rule-based validation powered by @rc-component/async-validator |
| Structure | Nested names, lists, dependencies, and preservation control |
| APIs | Form, Field, List, FormProvider, useForm, and useWatch |
| Runtime | React DOM and React Native friendly form state model |
Install
npm install @rc-component/form
The package was formerly documented as rc-field-form; use
@rc-component/form for new installs.
Usage
import Form, { Field } from '@rc-component/form';
const Input = ({ value = '', ...props }) => <input value={value} {...props} />;
const Demo = () => {
return (
<Form
onFinish={values => {
console.log('Finish:', values);
}}
>
<Field name="username">
<Input placeholder="Username" />
</Field>
<Field name="password">
<Input placeholder="Password" />
</Field>
<button>Submit</button>
</Form>
);
};
export default Demo;
Examples
Run the local dumi site:
npm install
npm start
Then open http://localhost:8000.
API
We use typescript to create the Type definition. You can view directly in IDE. But you can still check the type definition here.
Form
| Prop | Description | Type | Default |
|---|---|---|---|
| component | Customize Form render component | string | Component | false | form |
| fields | Control Form fields status. Only use when in Redux | FieldData[] | - |
| form | Set form instance created by useForm | FormInstance | Form.useForm() |
| initialValues | Initial value of Form | Object | - |
| name | Config name with FormProvider | string | - |
| preserve | Preserve value when field removed | boolean | false |
| validateMessages | Set validate message template | ValidateMessages | - |
| onFieldsChange | Trigger when any value of Field changed | (changedFields, allFields) => void | - |
| onFinish | Trigger when form submit and success | (values) => void | - |
| onFinishFailed | Trigger when form submit and failed | ({ values, errorFields, outOfDate }) => void | - |
| onValuesChange | Trigger when any value of Field changed | (changedValues, values) => void | - |
Field
| Prop | Description | Type | Default |
|---|---|---|---|
| dependencies | Will re-render if dependencies changed | NamePath[] | - |
| getValueFromEvent | Specify how to get value from event | (..args: any[]) => any | - |
| getValueProps | Customize additional props with value. This prop will disable valuePropName | (value) => any | - |
| initialValue | Field initial value | any | - |
| name | Field name path | NamePath | - |
| normalize | Normalize value before update | (value, prevValue, prevValues) => any | - |
| preserve | Preserve value when field removed | boolean | false |
| rules | Validate rules | Rule[] | - |
| shouldUpdate | Check if Field should update | boolean | (prevValues, nextValues) => boolean | - |
| trigger | Collect value update by event trigger | string | onChange |
| validateTrigger | Config trigger point with rule validate | string | string[] | onChange |
| valuePropName | Config value mapping prop with element | string | value |
List
| Prop | Description | Type | Default |
|---|---|---|---|
| name | List field name path | NamePath[] | - |
| children | Render props for listing fields | (fields: { name: NamePath }[], operations: ListOperations) => ReactNode | - |
useForm
Form creates a form instance with Form.useForm by default. You can also create the instance yourself and pass it to Form to call methods directly.
const Demo = () => {
const [form] = Form.useForm();
return <Form form={form} />;
};
For class components, use ref to get the form instance:
class Demo extends React.Component {
setRef = form => {
// Form instance here
};
render() {
return <Form ref={this.setRef} />;
}
}
| Prop | Description | Type |
|---|---|---|
| getFieldValue | Get field value by name path | (name: NamePath) => any |
| getFieldsValue | Get list of field values by name path list | (nameList?: (NamePath[]) => any) | true |
| getFieldError | Get field errors by name path | (name: NamePath) => string[] |
| getFieldsError | Get list of field errors by name path list | (nameList?: NamePath[]) => FieldError[] |
| isFieldsTouched | Check if list of fields are touched | (nameList?: NamePath[], allTouched?: boolean) => boolean |
| isFieldTouched | Check if a field is touched | (name: NamePath) => boolean |
| isFieldValidating | Check if a field is validating | (name: NamePath) => boolean |
| resetFields | Reset fields status | (fields?: NamePath[]) => void |
| setFields | Set fields status | (fields: FieldData[]) => void |
| setFieldsValue | Set fields value | (values) => void |
| submit | Trigger form submit | () => void |
| validateFields | Trigger fields to validate | (nameList?: NamePath[], options?: ValidateOptions) => Promise |
FormProvider
| Prop | Description | Type | Default |
|---|---|---|---|
| validateMessages | Config global validateMessages template | ValidateMessages | - |
| onFormChange | Trigger by named form fields change | (name, { changedFields, forms }) => void | - |
| onFormFinish | Trigger by named form fields finish | (name, { values, forms }) => void | - |
Interface
NamePath
| Type |
|---|
| string | number | (string | number)[] |
FieldData
| Prop | Type |
|---|---|
| touched | boolean |
| validating | boolean |
| errors | string[] |
| name | string | number | (string | number)[] |
| value | any |
Rule
| Prop | Type |
|---|---|
| enum | any[] |
| len | number |
| max | number |
| message | string |
| min | number |
| pattern | RegExp |
| required | boolean |
| transform | (value) => any |
| type | string |
| validator | (rule, value, callback: (error?: string) => void, form) => Promise | void |
| whitespace | boolean |
| validateTrigger | string | string[] |
validator
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.
ListOperations
| Prop | Type |
|---|---|
| add | (initValue: any) => void |
| remove | (index: number) => void |
ValidateMessages
Validate Messages provides a list of error template. You can ref here for fully default templates.
| Prop | Description |
|---|---|
| enum | Rule enum prop |
| len | Rule len prop |
| max | Rule max prop |
| min | Rule min prop |
| name | Field name |
| pattern | Rule pattern prop |
| type | Rule type prop |
Different with rc-form
rc-field-form is try to keep sync with rc-form in api level, but there still have something to change:
1. Field will not keep snyc with initialValues when un-touched
In rc-form, field value will get from initialValues if user not operate on it.
It's a bug but user use as a feature which makes fixing will be a breaking change and we have to keep it.
In Field Form, this bug will not exist anymore. If you want to change a field value, use setFieldsValue instead.
2. Remove Field will not clean up related value
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.
3. Nest name use array instead of string
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.
Field Form will only trade ['user', 'name'] to be { user: { name: 'Bamboo' } }, and user.name to be { ['user.name']: 'Bamboo' }.
4. Remove validateFieldsAndScroll
Since findDomNode is marked as warning in StrictMode. It seems over control of Form component.
We decide to remove validateFieldsAndScroll method and you should handle it with you own logic:
<Form>
<Field name="username">
<input ref={this.inputRef} />
</Field>
</Form>
5. getFieldsError always return array
rc-form returns null when no error happen. This makes user have to do some additional code like:
(form.getFieldsError('fieldName') || []).forEach(() => {
// Do something...
});
Now getFieldsError will return [] if no errors.
6. Remove callback with validateFields
Since ES8 is support async/await, that's no reason not to use it. Now you can easily handle your validate logic:
async function() {
try {
const values = await form.validateFields();
console.log(values);
} catch (errorList) {
errorList.forEach(({ name, errors }) => {
// Do something...
});
}
}
Notice: Now if your validator return an Error(message), not need to get error by e => e.message. FieldForm will handle this.
7. preserve is default to false
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.
8. setFields not trigger onFieldsChange and setFieldsValue not trigger onValuesChange
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.
Additionally, user control update trigger onFieldsChange & onValuesChange event has potential dead loop risk.
Development
npm install
npm start
npm test
npm run lint
npm run tsc
npm run compile
npm run build
The dumi site runs at http://localhost:8000 by default.
Release
npm run prepublishOnly
The release flow is handled by @rc-component/np through the rc-np command after the package build.
License
@rc-component/form is released under the MIT license.
Top Related Projects
Build forms in React, without the tears 😭
📋 React Hooks for form state management and validation (Web + React Native)
🏁 High performance subscription-based form state management for React
A Higher Order Component using react-redux to keep form state in a Redux store
Performance-focused API for React forms 🚀
Convert
designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual Copilot