Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

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

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)