Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/darianstlex/efx-forms
Effector Forms Library
https://github.com/darianstlex/efx-forms
effector form form-validation forms react validation
Last synced: about 1 month ago
JSON representation
Effector Forms Library
- Host: GitHub
- URL: https://github.com/darianstlex/efx-forms
- Owner: darianstlex
- License: mit
- Created: 2021-12-21T12:23:23.000Z (about 3 years ago)
- Default Branch: main
- Last Pushed: 2024-09-09T19:03:40.000Z (3 months ago)
- Last Synced: 2024-10-10T06:54:06.017Z (2 months ago)
- Topics: effector, form, form-validation, forms, react, validation
- Language: TypeScript
- Homepage:
- Size: 397 KB
- Stars: 4
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# EFX-Forms
> Effector JS forms
There are some breaking changes starting from v2
## Installation
```bash
$ npm install efx-forms
```
Peer dependencies - library depends on:
> react effector effector-react lodash#### mjs build included
## Main Components
### Form / Field
```jsx
import { Form, Field } from 'efx-forms';
import { FormDataProvider } from 'efx-forms/FormDataProvider';
import { required, email } from 'efx-forms/validators';const Input = ({ id, label, error, errors, value, ...props }) => (
{label}
{error}
)const TextField = (props) =>
const validators = {
name: [required()],
}const Page = () => {
const submit = (values) => {
console.log(values);
}
return (
{[0, 1, 2].map((idx) => (
))}
{({ values }) => (
JSON.stringify(values)
JSON.stringify(shapeFy(values))
)}
Submit
)
}
```
```ts
// Form values
values = {
'name': 'John',
'email': '[email protected]',
'address[0]': 'First Line',
'address[1]': 'Second Line',
'address[2]': 'Postcode',
}
valuesShape = {
'name': 'John',
'email': '[email protected]',
'address': [
'First Line',
'Second Line',
'Postcode',
]
}
```# Props
### Form component
```ts
interface Form {
// Form name - required, used to get form instance outside of context
name: string,
/**
* Form submit method - on validation success will be called with
* form values.
* If skipClientValidation is set - no validation will be applied.
* If submit return promise:
* - reject - object with errors per field - errors will be passed
* to the form
* { 'user.name': 'Name is already taken', ... }
* - resolve - success submit
* @param values - FormValues - flat
* @example
* { 'user.name': 'John', 'user.age': '20' }
*/
onSubmit?: (values: Record) => void | Promise>;
// If set, submit will skip client form validation
// Default: false
skipClientValidation?: boolean;
// Form initial values - field initialValue is in priority
initialValues?: { fieldName: 'value' }
// Keep form data on unmount
// Default: false
keepOnUnmount: boolean;
// PROPERTY - serialize - serialize stores
// not reactive, initialized only once on form creation
// Default: false
serialize?: boolean;
// Set fields validation behavior onBlur
// Default: true
validateOnBlur?: boolean;
// Set fields validation behavior onChange
// Default: false
validateOnChange?: boolean;
// Disable reinit on initialValue change
disableFieldsReinit?: boolean;
// Validators config per field - field validators are in priority
validators?: {
fieldName: [
(value: any, values: Record) => string | false,
]
};
}
```### Field component
```ts
interface Field {
// Field name - required, used to register/get field in the form
name: string,
// Field initial value - used on initial load and reset
// default = ''
initialValue?: any;
// Transform value before set to store
parse?: (value: any) => any;
// Format value before displaying
format?: (value: any) => any;
// Passive field does not update its active state and config
passive?: boolean;
// Validators array - applied on validation
validators?: [
(value: any, values: Record) => string | false,
];
// Set validation behaviour onBlur, overrides form value
// Default: true
validateOnBlur?: boolean;
// Set validation behaviour onChange, overrides form value
// Default: false
validateOnChange?: boolean;
// Disable reinit on initialValue change
disableFieldReinit?: boolean;
// Field component - component to be used as form field
Field: ReactComponent;
// Form name - if field belongs to a different form or used outside
// of the form context
formName?: string;
}
```### IfFormValues component
Conditional rendering based on form values
```ts
interface IfFormValues {
children?: ReactNode;
// Form name - used to get form values,
// if not provided will be taken from context
form?: string;
// Condition check - accepts form values and return boolean,
// if true render children
check: (values: Record, activeValues: Record) => boolean;
// Set fields values on show - { fieldName: 'value' }
setTo?: Record;
// Set fields values on hide - { fieldName: 'value' }
resetTo?: Record;
// Debounce for fields update
// Default: 0
updateDebounce?: number;
// Render prop - accepts form values and return react element
// if defined will be used instead of children
render?: (values: Record) => ReactElement;
}
``````jsx
import { IfFormValues } from 'efx-forms/IfFormValues';const ConditionalRender = () => (
age > 21 }>
Hey, I am here
);const ConditionalRenderProp = () => (
age > 21 }
render={({ age, name }) =>Hi, I am {name} - {age}}
/>
);
```### FormDataProvider component
Subscribe for form values changes
```ts
interface FormDataProvider {
// Render function - provides all subscribed data
children: (values: ReturnType) => ReactNode;
// Form name if used outside of context or refers to another form
name?: string;
}
```
```jsx
import { FormDataProvider } from 'efx-forms/FormDataProvider';const FormData = () => (
{({ values, errors }) =>{values} - {errors}}
);
```### IfFieldValue component
Conditional rendering based on field value
```ts
interface IfFieldValue {
children?: ReactNode;
// Field name
field: string;
// Form name - used to get form values,
// if not provided will be taken from context
formName?: string;
// Condition check - accepts form values and return boolean,
// if true render children
check: (value: any) => boolean;
// Render prop - accepts form values and return react element
// if defined will be used instead of children
render?: (values: any) => ReactElement;
}
``````jsx
import { IfFieldValue } from 'efx-forms/IfFieldValue';const ConditionalRender = () => (
age > 21 }>
Hey, I am here
);const ConditionalRenderProp = () => (
age > 21 }
render={(age) =>Hi, I am {age}}
/>
);
```### FieldDataProvider component
Subscribe for field value changes```ts
interface FieldDataProvider {
// Render function - provides all subscribed data
children: (values: ReturnType) => ReactNode;
// Field name to get stores values from
name: string;
// Form name if used outside of context or refers to another form
formName?: string;
}
```
```jsx
import { FieldDataProvider } from 'efx-forms/FieldDataProvider';const FieldData = () => (
{({ value, active }) =>{value} - {active}}
);
```# Instances
Form Instance
```ts
interface FormInstance {
/** PROPERTY - Form name */
domain: Domain;
/** PROPERTY - Form name */
name: string;
/** $$STORE - Form active fields - all fields statuses - flat */
$active: Store>;
/** $$STORE - Form active only fields - flat */
$activeOnly: Store>;
/** $$STORE - Form active values - all active / visible fields values - flat */
$activeValues: Store>;
/** $$STORE - Form values - all fields values - flat */
$values: Store>;
/** $$STORE - Form errors - all field errors */
$errors: Store>;
/** $$STORE - Form errors - fields last error - flat */
$error: Store>;
/** $$STORE - Form valid - true if form is valid */
$valid: Store;
/** $$STORE - Form submitting - true if busy */
$submitting: Store;
/** $$STORE - Form touched - true if touched */
$touched: Store;
/** $$STORE - Form touches - all fields touches - flat */
$touches: Store>;
/** $$STORE - Form dirty - true if diff from initial value */
$dirty: Store;
/** $$STORE - Form dirties - all fields dirty state - flat */
$dirties: Store>;
/** PROP - Form config */
config: IFormConfig;
/** PROP - Form config */
configs: Record;
/** EVENT - Form erase - reset form and delete all assigned form data */
erase: EventCallable;
/** EVENT - Form onChange event */
onChange: EventCallable<{ name: string; value: any; }>;
/** EVENT - Form onBlur event */
onBlur: EventCallable<{ name: string; value: any; }>;
/** EVENT - Form reset - resets form to initial values */
reset: EventCallable;
/** EVENT - Field reset - resets field to initial value */
resetField: EventCallable;
/** EVENT - Reset untouched fields to initial values */
resetUntouched: EventCallable;
/** EVENT - Set form config */
setActive: EventCallable<{ name: string; value: boolean; }>;
/** METHOD - Set form config */
setConfig: (cfg: IFormConfig) => void;
/** METHOD - Set field config */
setFieldConfig: (cfg: IFieldConfig) => void;
/** EVENT - Form update field values */
setValues: EventCallable>;
/**
* EFFECT - Form submit - callback will be called with form values if form is valid
* or if callback returns promise reject with errors, will highlight them in the form
*/
submit: Effect;
/** EVENT - Form validate trigger */
validate: EventCallable;
}
```# Methods / Hooks
```ts
import { getForm, useFormInstance } from 'efx-forms';
import { useForm } from 'efx-forms/useForm';
import { useFormData } from 'efx-forms/useFormData';
import { useFormValues } from 'efx-forms/useFormValues';
import { useFormStore } from 'efx-forms/useFormStore';
import { useFormStores } from 'efx-forms/useFormStores';
import { useFormMethods } from 'efx-forms/useFormMethods';
import { useField } from 'efx-forms/useField';
import { useFieldData } from 'efx-forms/useFieldData';
import { useFieldStore } from 'efx-forms/useFieldStore';
import { useStoreProp } from 'efx-forms/useStoreProp';
import { useStorePropFn } from 'efx-forms/useStorePropFn';/**
* Return form by name
* @type (config: IFormConfig) => IForm
*/
const formOne = getForm({ name: 'form-one' });/**
* Hook - return form (from context) data/methods or provided form by name.
* Form name is needed when hook is used outside of the form context
* or refers to another form.
* Result includes all form data in plain objects and units in scope
* @type (formName?: string) => ReturnType
*/
const formTwo = useForm();/**
* Hook - return form (from context) data or provided form by name.
* Form name is needed when hook is used outside of the form context
* or refers to another form.
* Result includes all form data in plain objects and units in scope
* @type (formName?: string) => ReturnType
*/
const formThree = useFormData();/**
* Hook - return form (from context) instance or provided form by name.
* Form name is needed when hook is used outside of the form context
* or refers to another form.
* Result contains all form stores and units, use useUnit to get values
* @type (formName?: string) => ReturnType
*/
const formInst = useFormInstance();/**
* Hook - return form (from context) store values or from provided form.
* Form name is needed when hook is used outside of the form context
* or refers to another form.
* @type (store: string, formName?: string) => IFormErrors
*/
const formErrors = useFormStore('$errors');/**
* Hook - return form (from context) stores values array or from
* provided form. Form name is needed when hook is used outside of the
* form context or refers to another form.
* @type (store: string[], formName?: string) => IFormErrors
*/
const [errors, values] = useFormStores(['$errors', '$values']);/**
* Hook - return form (from context) values or from provided form.
* Form name is needed when hook is used outside of the form context
* or refers to another form.
* @type (formName?: string) => IFormValues
*/
const formValues = useFormValues();/**
* Hook - return form (from context) methods or from provided form.
* Form name is needed when hook is used outside of the form context
* or refers to another form.
* @type (formName?: string) => ReturnType
*/
const formMethods = useFormMethods();/**
* Hook - return field by name
* Form name is needed when hook is used outside of the form context
* or refers to another form.
* @type (name: string, formName?: string) => ReturnType
*/
const field = useField('field-one');/**
* Hook - return field value by name
* Form name is needed when hook is used outside of form context
* or refers to another form.
* @type (name: string, formName?: string) => ReturnType
*/
const fieldData = useFieldData('field-one');/**
* Hook - return field store value
* Form name is needed when hook is used outside of form context
* or refers to another form.
* @type (data: {
* store: string;
* name: string;
* formName?: string;
* defaultValue?: any;
* }) => ReturnType
*/
const fieldActive = useFieldStore({
store: '$active',
name: 'user.name',
formName: 'login',
defaultValue: '',
});/**
* Hook - return store value
* @type (
* store: Store,
* prop: string,
* defaultValue?: any,
* ) => ReturnType
*/
const storePropValue = useStoreProp(form.$values, 'user.name', '');/**
* Hook - return store value
* @type (
* store: Store,
* getter: (value: any) => any,
* defaultValue?: any,
* ) => ReturnType
*/
const storePropFnValue = useStorePropFn(form.$values, (val) => val.name, '');
```# Utils
```ts
import {
// effector forms domain, usefull for logging / debugging
domain,
truthyFy,
shapeFy,
truthyFyStore,
shapeFyStore,
flattenObjectKeys,
} from 'efx-forms/utils';/**
* Return only truthy values from object
* @type (values: IFormValues) => IFormValues
*/
const truthyValues = truthyFy(values);/**
* Return flat to shaped values
* @type (values: IFormValues) => {}
* @example
* { 'user.name': 'John' } => { user: { name: 'John } }
*/
const shapedValues = shapeFy(values);/**
* Return effector store with truthy values
* @type ($values: Store): Store => $truthyValues
*/
const $truthyStore = truthyFyStore($values);/**
* Return effector store with shaped values
* @type ($values: Store): Store => $shapedValues
*/
const $shapedStore = shapeFyStore($values);/**
* Return flatten one level object keys/values
* helper for the nested initial values
* @type (values: Record): Record => ({})
*/
const initialValues = flattenObjectKeys(values);
```# Validators
Check validators.d.ts file to see all built-in validators and their arguments
```ts
import { required, email } from 'efx-forms/validators';const formValidations = {
'user.name': [required()],
'user.email': [
required({ msg: 'Email is required' }), // custom message
email(),
],
}
```[Examples](https://github.com/darianstlex/efx-forms-cra)