@rn-form/form
v3.5.1
Published
react native form
Readme
Click here to Example
API
Form or Form.ScrollView Support scroll to the first error field
type Form = {
form: FormInstance<T>;
colon?: boolean;
initialValues?: {[key: string]: any}; //default values
labelAlign?: 'left' | 'right'; //The text align of label of all items
name?: string; //Form name. Will be the prefix of Field id
preserve?: boolean; //Keep field value even when field removed
requiredMark?: boolean | string; /
requiredMarkStyle?: StyleProp<TextStyle>;
requiredMarkPosition?: 'before' | 'after';
validateMessages?: ValidateMessages; //Validation prompt template
validateTrigger?: TriggerAction | 'onChange' | 'onBlur'; //Config field validate trigger
onValuesChange?: (values: {[key: string]: any}) => void; //Trigger when value updated
keyboardManager?: boolean; //Keep the focused input above the keyboard using native code
onFormDispose?: (info: {
values: {[key: string]: any};
errors: {[key: string]: string | undefined};
isChanged: boolean;
}) => void; //Trigger when form unmount
errorStyle?: StyleProp<TextStyle>;
labelStyle?: StyleProp<TextStyle>;
style?: StyleProp<ViewStyle>;
};type Form.ScrollView = Form & ScrollViewProps;- form
<FormInstance>Click to here
Form control instance created by Form.useForm() with Hooks
or Form.create(Component) with Class Component.
Automatically created when not provided- colon
<Boolean>
Configure the default value of colon for Form.Item.
Indicates whether the colon after the label is displayed
(only effective when prop layout is horizontal)- initialValues
{[key: string]: any}
Default Values by form- labelAlign
<'left' | 'right'>
The text align of label of all items- name
<String>
Form name. Will be the prefix of Field id- preserve
<Boolean>
Keep field value even when field removed- requiredMark
<Boolean | String>
Required mark style. Can use required mark or optional mark.
You can not config to single Form.Item since this is a Form level config- requiredMarkStyle
<TextStyle>
Custom mark styleSheet- requiredMarkPosition
<'before' | 'after'>
- Default: before
Config position required mark or optional mark- validateMessages
<ValidateMessages>
Validation prompt template
const validateMessages = {
required: '{{name}} is required',
whitespace: '{{name}} cannot be empty',
len: '{{name}} must be exactly {{len}} characters',
min: '{{name}} must be at least {{min}}',
max: '{{name}} cannot be greater than {{max}}',
pattern: '{{name}} does not match pattern {{pattern}}',
enum: '{{name}} must be one of {{enum}}',
};- validateTrigger
<'onChange' | 'onBlur'>
- Default: onChange
Config field validate trigger- onValuesChange
(values: {[key: string]: any}) => void
Trigger when value updated- keyboardManager
<Boolean>
<Form.ScrollView form={form} keyboardManager>
<Form.Item name="email">
<Input />
</Form.Item>
</Form.ScrollView>iOS only. Enable the native keyboard manager for this Form or Form.ScrollView. It
follows the native first responder and moves or scrolls the form just enough to keep
the focused input above the keyboard. No JavaScript focus or keyboard listeners are
installed.
On Android the prop renders a plain View and does nothing, the platform already keeps
the focused input visible through the windowSoftInputMode of the activity.
This prop only turns the manager on and off. Everything else (distance, accessory
toolbar, keyboard appearance) is configured once on FormProvider with the
keyboardManager config. <a href="#formprovider">Click to here</a>
Requires React Native 0.78 or newer, the version that both consumes the
codegenConfig ios.componentProvider entry of this package and ships React 19.
The native module is autolinked. Run `pod install` after installing or upgrading the
package on iOS, then rebuild the native application. The native view is declared with
React Native Codegen and is a New Architecture/Fabric component only, it is not
registered when the application runs with `newArchEnabled=false`.- onFormDispose
(info: { values, errors, isChanged }) => void
Trigger when form unmount.
Returns current values, field errors, and whether values changed from initialValues.- errorStyle
<TextStyle>
Configure the label message error form- style
<ViewStyle>
Configure the style formForm.Item
! Must be inside the Form
type FormItem = {
name: T;
label?: string; //Label text
initialValue?: any; //Initial value for this field
rules?: Rule[];
required?: boolean; //Display required style. It will be generated by the validation rule
validateTrigger?:
| TriggerAction.onBlur
| TriggerAction.onChange<'onChange' | 'onBlur'>; //When to validate the value of children node
preserve?: boolean; //Keep field value even when field removed
style?: StyleProp<ViewStyle>;
getValueProps?: (v: any) => any;
normalize?: (v: any) => any;
children: ((handle: FormItemHandle) => ReactNode) | React.ReactNode;
};
type FormItemHandle<V = any> = {
name: string;
value?: V;
error?: string;
onChangeValue: (value: V) => void;
onBlur: () => void;
};- name
<String>* Required
Field name- label
<String>
Label text- initialValue
<any>
Config sub default value. Form initialValues get higher priority when conflict- required
<Boolean>
Display required style. It will be generated by the validation rule- validateTrigger
TriggerAction.onBlur | TriggerAction.onChange <'onChange' | 'onBlur'>
Default: onChange
When to validate the value of children node- rules
<Rule[]>
type Rule = {
required?: boolean; //Display required style. It will be generated by the validation rule
enum?: any[]; //Match enum value. You need to set type to enum to enable this
len?: number; //Length of string
max?: number; // max of number
min?: number; // min of number
message?: string; //Error message. Will auto generate by template if not provided
pattern?: RegExp; //Regex pattern
transform?: (value: any) => any; //Transform value to the rule before validation
validateTrigger?:
| TriggerAction.onBlur
| TriggerAction.onChange<'onChange' | 'onBlur'>;
validator?: (
rule: Rule & {name: string},
value: any,
callback: (errorMessage?: string) => void,
) => Promise<void | Error>; //Customize validation rule. Accept Promise as return. See example
whitespace?: boolean; //Failed if only has whitespace
};//Rules for field validation.
import React, {Component} from 'react';
import Form, {Input} from '@form-rn/form';
class App extends Component {
render() {
return (
<Form.ScrollView>
<Form.Item
required={true}
validateTrigger="onBlur"
name="field_name"
label="Example Children type of JSX.Element"
initialValue="initialValue"
rules={[
{required: true, message: 'Field is required'},
{
validator: (rule, value) => {
if (value) {
return Promise.resolve();
}
return Promise.reject('Field is required');
},
validateTrigger: 'onBlur',
},
{
validator: (rule, value, callback) => {
if (value) {
callback();
} else {
callback('Field is required');
}
},
},
]}>
<Input placeholder="2" />
</Form.Item>
</Form.ScrollView>
);
}
}- children
<JSX.Element | (handle: FormItemHandle) => any>
//custom component reading the field with the hook
import Form, {useFormItem} from '@rn-form/form';
const Rating = () => {
const {value, error, onChangeValue, onBlur} = useFormItem<number>();
return <Stars value={value} onPress={onChangeValue} onEnd={onBlur} invalid={!!error} />;
};
<Form.Item name="rating" label="Rating">
<View style={styles.row}>
<Rating />
</View>
</Form.Item>;//children was supported type of functional
//onBlur must be required when rules.trigger or validateTrigger is onBlur
import React, {Component} from 'react';
import Form, {Input} from '@form-rn/form';
class App extends Component {
render() {
return (
<Form.ScrollView>
<Form.Item
name="children"
initialValue="12"
label="Example Children type of function">
{({onChangeValue, value, onBlur, error}) => {
console.log(value, onBlur, error);
return (
<Input
placeholder="2"
onChangeValue={onChangeValue}
value={value}
onBlur={onBlur}
error={error}
/>
);
}}
</Form.Item>
</Form.ScrollView>
);
}
}- colon
<Boolean>
Used with label, whether to display : after label text.- getValueProps
<(value: any) => any>
Transform stored value before passing to child component (stored value → display value).- normalize
<(value: any) => any>
Transform input value before storing (input value → stored value).
The inverse of getValueProps.
Applied in onChangeValue, setFieldValue, setFieldsValue, resetFields, and validateFields.- labelAlign
<'left' | 'right'>
The text align of label- style
<ViewStyle | ViewStyle[]>
Stylesheet Field- preserve
<Boolean>
Keep field value even when field removedFormInstance
If you code like below then all the function of FormInstance will return about under an object like {idForm1: values, idForm2: values}
const form1 = useForm<T>()
const form2 = useForm<T>()
<Form form={form}>
<Form.Item name="field_name" label="Example">
<Input placeholder="2" />
</Form.Item>
</Form>
<Form form={form2}>
<Form.Item name="field_name2" label="Example2">
<Input placeholder="2" />
</Form.Item>
</Form>- getFieldError
(name: string) => string | undefined
Get the error messages by the field name- getFieldsError
(names?: string[]) => Promise<{[key: string]: string | undefined}>
Get the error messages by the fields name. Return as an array- getFieldsValue
(names?: string[], filter?: (meta: { touched: boolean, validating: boolean }) => boolean) => Promise<any>
Get values by a set of field names.
Return according to the corresponding structure.
Default return mounted field value, but you can use getFieldsValue() to get all values- getFieldValue
(name: string) => Promise<any>
Get the value by the field name- isFieldsTouched
(names?: string[], allTouched?: boolean) => boolean
Check if fields have been operated.
Check if all fields is touched when allTouched is true- isFieldTouched
(name: string) => boolean
Check if a field has been operated- isValuesChanged
(names?: string[]) => boolean
Check whether current values differ from initialValues.
Pass field names to limit the comparison.- resetFields
(fields?: string[]) => Promise<void>
Reset fields to initialValuessetFieldError
(name: string, error?: string | false) => voidsetFieldValue
(name: string, value: any) => void
Set fields value(Will directly pass to form store.
If you do not want to modify passed object, please clone first)- setFieldsValue
(values: {[key: string]: any}) => Promise<any>
Set fields value(Will directly pass to form store.
If you do not want to modify passed object, please clone first).- validateFields
(names?: string[]) => Promise<ValueValidateField>
type ValueValidateField = {
values: T;
errors?: {[key: string]: string};
};Validate fields and return errors and valuesFormProvider
Global configuration for all Forms.
type FormProviderProps = {
Text?: React.ComponentType<any>; // Custom Text component for Labels and Errors
errorStyle?: StyleProp<TextStyle>; // Global error style
labelStyle?: StyleProp<TextStyle>; // Global label style
requiredMark?: boolean | string; // Global required mark
requiredMarkStyle?: StyleProp<TextStyle>; // Global required mark style
requiredMarkPosition?: 'before' | 'after'; // Global required mark position
validateMessages?: ValidateMessages; // Global validation messages
keyboardManager?: KeyboardManagerConfig; // Global keyboard manager config
};- Text
<Component>
Custom Text component for Labels and Errors- errorStyle
<TextStyle>
Global error style- labelStyle
<TextStyle>
Global label style- requiredMark
<Boolean | String>
Global required mark- requiredMarkStyle
<TextStyle>
Global required mark style- requiredMarkPosition
<'before' | 'after'>
Global required mark position- validateMessages
<ValidateMessages>
Global validation messages- keyboardManager
<KeyboardManagerConfig>
//iOS only, every key is ignored on Android
type KeyboardManagerConfig = {
distance?: number; //Space kept between the focused input and the bar or the keyboard. Default 12
toolbar?: boolean; //Show an accessory bar above the keyboard. Default false
toolbarDoneText?: string; //Label of the done button. Default 'Done'
toolbarPreviousNext?: boolean; //Show the arrows moving to the previous/next input. Default false
toolbarPlaceholder?: boolean; //Show the placeholder of the focused input inside the bar. Default false
toolbarTintColor?: ColorValue; //Color of the bar buttons
toolbarBarTintColor?: ColorValue; //Background color of the bar
keyboardAppearance?: 'default' | 'light' | 'dark'; //Force the keyboard appearance of every input inside the Form
};- Default:
const defaultKeyboardManager = {
distance: 12,
toolbar: false,
toolbarDoneText: 'Done',
toolbarPreviousNext: false,
toolbarPlaceholder: false,
toolbarTintColor: undefined, //Keep the native tint of the bar
toolbarBarTintColor: undefined, //Keep the native background of the bar
keyboardAppearance: undefined, //Keep the appearance each input asked for
};<FormProvider
keyboardManager={{
distance: 10,
toolbar: true,
toolbarDoneText: 'Done',
toolbarPreviousNext: true,
toolbarPlaceholder: true,
toolbarTintColor: '#0000ff',
toolbarBarTintColor: '#ffffff',
keyboardAppearance: 'light',
}}>
<App />
</FormProvider>Configure the native keyboard manager once for every Form of the application.
Turn it on per form with the keyboardManager prop of Form or Form.ScrollView,
this config alone does nothing.
Every key is merged over defaultKeyboardManager, so passing a partial config
keeps the default of every key left out. Import defaultKeyboardManager from the
package to read or spread those values.
The whole config is iOS only, it drives the native view and its
inputAccessoryView. Android ships no native code for this and ignores the
config. The toolbar is never installed on an input that already carries an
accessory view of the application.
distance is measured from the top of the toolbar when the toolbar is on,
because iOS reports the accessory view as part of the keyboard frame.Input <TextInputProps>
https://reactnative.dev/docs/textinput
- error
<Boolean>
Preview border error input- style
<ViewStyle>
Stylesheet wrap view Input- styleInput
<TextStyle>
Stylesheet InputRadio
RadioGroup
Example
//Function Componet
import React from 'react';
import Form, {Input} from '@form-rn/form';
import {Button} from 'react-native';
import Text from 'components/Text' // your custom component Text
const App = () => {
const form = useForm<Data>();
return (
<FormProvider Text={Text}>
<Form.ScrollView form={form} initialValues={{example: 'example'}}>
<Form.Item<Data> name="example" initialValue="12" label="Example">
<Input placeholder="2" />
</Form.Item>
<Form.Item<Data> name="radioGroup" label="Login2" required validateFirst>
<RadioGroup>
<Radio value={1} label="radio 1" />
<Radio value={2} label="radio 2" />
<Radio value={3} label="radio 3" />
</RadioGroup>
</Form.Item>
<Form.Item<Data> name="radio" label="Login2" required validateFirst>
<Radio label="radio 1" />
</Form.Item>
<Form.Item<Data> name="function_child" label="Function Child" initialValue="12">
{({onChangeValue, value, onBlur, error}) => (
<Input
onChangeValue={onChangeValue}
value={value}
onBlur={onBlur}
error={error}
/>
)}
</Form.Item>
<Button
title="Get Value"
onPress={async () => {
const error = await form.getFieldError();
const errors = await form.getFieldsError();
const errorsWithNames = await form.getFieldsError(['example']);
const values = await form.getFieldsValue();
const valuesWithNames = await form.getFieldsValue(['example']);
const valuesWithFilter = await form.getFieldsValue(
undefined,
(touched, validating) => touched && validating,
);
const value = await form.getFieldValue('example');
const isToucheds = await form.isFieldsTouched(undefined, true);
const isTouched = await form.isFieldTouched('example');
const isValidating = await form.isFieldValidating('example');
const data = await form.validateFields();
if (Array.isArray(data)) {
console.log('data is array', data);
} else console.log(data);
}}
/>
<Button
title="Control Value"
onPress={async () => {
form.resetFields();
form.resetFields(['example']);
form.setFields([
{
name: 'example',
value: 'example1',
touched: true,
validating: true,
},
]);
form.setFieldValue('example', 'example1');
form.setFieldsValue({example: 'example1'});
}}
/>
</Form.ScrollView>
</FormProvider>
);
};
export default App;
//Class Component
import React from 'react';
import Form, {Input} from '@form-rn/form';
import {Button} from 'react-native';
class App extends Component<{form: FormInstance<Data>}> {
render() {
const {form} = this.props;
return (
<Form.ScrollView initialValues={{example: 'example'}}>
<Form.Item name="example" initialValue="12" label="Example">
<Input placeholder="2" />
</Form.Item>
<Form.Item name="function_child" label="Function Child" initialValue="12">
{({onChangeValue, value, onBlur, error}) => (
<Input
onChangeValue={onChangeValue}
value={value}
onBlur={onBlur}
error={error}
/>
)}
</Form.Item>
<Button
title="Get Value"
onPress={async () => {
const error = await form.getFieldError();
const errors = await form.getFieldsError();
const errorsWithNames = await form.getFieldsError(['example']);
const values = await form.getFieldsValue();
const valuesWithNames = await form.getFieldsValue(['example']);
const valuesWithFilter = await form.getFieldsValue(
undefined,
(touched, validating) => touched && validating,
);
const value = await form.getFieldValue('example');
const isToucheds = await form.isFieldsTouched(undefined, true);
const isTouched = await form.isFieldTouched('example');
const isValidating = await form.isFieldValidating('example');
const data = await form.validateFields();
if (Array.isArray(data)) {
console.log('data is array', data);
} else console.log(data);
}}
/>
<Button
title="Control Value"
onPress={async () => {
form.resetFields();
form.resetFields(['example']);
form.setFields([
{
name: 'example',
value: 'example1',
touched: true,
validating: true,
},
]);
form.setFieldValue('example', 'example1');
form.setFieldsValue({example: 'example1'});
}}
/>
</Form.ScrollView>
);
}
}
export default Form.create(App);
// multiple Form
import React from 'react';
import Form, {Input} from '@form-rn/form';
import {Button} from 'react-native';
const App = () => {
const form1 = Form.useForm()
const form2 = Form.useForm()
return (
<Form form={form1}>
<Form.Item name="example" initialValue="12" label="Example">
<Input placeholder="2" />
</Form.Item>
</Form>
<Form form={form2}>
<Form.Item name="example" initialValue="12" label="Example">
<Input placeholder="2" />
</Form.Item>
</Form>
<Button
title="Control Value"
onPress={async () => {
form1.resetFields();
form2.resetFields();
}}
/>
)
}
