{"id":13518617,"url":"https://github.com/shakacode/react-validation-layer","last_synced_at":"2025-10-11T05:50:28.196Z","repository":{"id":55844896,"uuid":"53430152","full_name":"shakacode/react-validation-layer","owner":"shakacode","description":"An opinionated form validation tool for React apps","archived":false,"fork":false,"pushed_at":"2020-12-11T10:13:30.000Z","size":545,"stargazers_count":11,"open_issues_count":12,"forks_count":0,"subscribers_count":5,"default_branch":"master","last_synced_at":"2024-05-01T09:45:47.872Z","etag":null,"topics":["async-validation","form-validation","forms","react","redux"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/shakacode.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-03-08T17:07:41.000Z","updated_at":"2018-12-17T18:52:50.000Z","dependencies_parsed_at":"2022-08-15T07:50:31.444Z","dependency_job_id":null,"html_url":"https://github.com/shakacode/react-validation-layer","commit_stats":null,"previous_names":[],"tags_count":16,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shakacode%2Freact-validation-layer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shakacode%2Freact-validation-layer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shakacode%2Freact-validation-layer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shakacode%2Freact-validation-layer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/shakacode","download_url":"https://codeload.github.com/shakacode/react-validation-layer/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246453721,"owners_count":20779998,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["async-validation","form-validation","forms","react","redux"],"created_at":"2024-08-01T05:01:46.916Z","updated_at":"2025-10-11T05:50:23.179Z","avatar_url":"https://github.com/shakacode.png","language":"JavaScript","readme":"# react-validation-layer\n\n[![npm version](https://img.shields.io/npm/v/react-validation-layer.svg?style=flat-square)](https://www.npmjs.com/package/react-validation-layer)\n[![build status](https://img.shields.io/travis/shakacode/react-validation-layer/master.svg?style=flat-square)](https://travis-ci.org/shakacode/react-validation-layer)\n[![dependencies status](https://img.shields.io/gemnasium/shakacode/react-validation-layer.svg?style=flat-square)](https://gemnasium.com/shakacode/react-validation-layer)\n[![license](https://img.shields.io/npm/l/react-validation-layer.svg?style=flat-square)](https://www.npmjs.com/package/react-validation-layer)\n\nAn opinionated form validation tool for React apps.\n\n\n## Why\n\n* **Great UX out of the box**\u003cbr\u003e\n  Offers predefined set of `strategies` to provide superior user experience, incl. debounced async validations.\n\n* **Framework agnostic**\u003cbr\u003e\n  It works with all state management tools (or without any). Only `React` is required.\n\n* **Thin**\u003cbr\u003e\n  Layer doesn't own the form data. Don't need to change anything in the way you manage the state to start using it. Its main and only concern is form validation with superior UX.\n\n* **Declarative**\u003cbr\u003e\n  All you have to do is to declare behavior via props and shape the look in the markup. Layer will take care of the rest.\n\n\n## Table of Contents\n\n* [Examples](#examples)\n* [Installation](#installation)\n* [Usage](#usage)\n* [Configuration](#configuration)\n* [Rendering](#rendering)\n* [Lifecycles](#lifecycles)\n* [WIPs \u0026 TODOs](#wips--todos)\n\n\n## Examples\n\n_Website is WIP and not ready for mobiles yet, sorry._\n\n* **Simple** [ [live](http://rvl.surge.sh/#/examples/login-form) \u0026middot; [source](website/src/examples/login-form) ]\u003cbr\u003e\n  Sync validations only.\n* **Advanced** [ [live](http://rvl.surge.sh/#/examples/signup-form) \u0026middot; [source](website/src/examples/signup-form) ]\u003cbr\u003e\n  Sync \u0026 async validations, linked fields etc.\n\n\n## Installation\n\nGet it:\n\n```shell\n# yarn\nyarn add react-validation-layer\n\n# npm\nnpm install --save react-validation-layer\n```\n\n\n## Usage\n\nAs simple as:\n\n```js\nimport ValidationLayer from 'react-validation-layer';\n\n\u003cValidationLayer\n  strategy=\"onFirstSubmit\"\n  data={{ email, password }}\n  fields={{\n    email: emailFieldConfig,\n    password: passwordFieldConfig,\n  }}\n  handlers={{\n    onChange: onChangeHandler,\n    onSubmit: onSubmitHandler,\n  }}\n\u003e\n  {layer =\u003e (\n    \u003cform onSubmit={layer.handleSubmit}\u003e\n      \u003cinput type=\"text\" {...layer.getPropsFor('email')} /\u003e\n      \u003cspan className={layer.getStatusFor('email')}\u003e\n        {layer.getMessageFor('email')}\n      \u003c/span\u003e\n\n      \u003cinput type=\"text\" {...layer.getPropsFor('password')} /\u003e\n      \u003cspan className={layer.getStatusFor('password')}\u003e\n        {layer.getMessageFor('password')}\n      \u003c/span\u003e\n\n      \u003cbutton {...layer.getSubmitButtonProps()}\u003e\n        Submit\n      \u003c/button\u003e\n    \u003c/form\u003e\n  )}\n\u003c/ValidationLayer\u003e\n```\n\n\n## Configuration\nLayer requires 3 things to handle validation of the form:\n* **data** to validate and render\n* **fields** list with configurations\n* and **onChange + onSubmit handlers**.\n\nSome options can be set either on the global level (i.e. for all fields), or on the `field` level. `field`-level option has priority and overrides the global one.\n\n* [`id`](#propsid)\n* [**`data`**](#propsdata)\n* [**`fields`**](#propsfields)\n    * [`field.strategy`](#fieldstrategy)\n    * [`field.asyncStrategy`](#fieldasyncstrategy)\n    * [`field.validate`](#fieldvalidate)\n    * [`field.validateAsync`](#fieldvalidateasync)\n    * [`field.debounceInterval`](#fielddebounceinterval)\n    * [`field.linkedFields`](#fieldlinkedfields)\n    * [`field.filter`](#fieldfilter)\n    * [`field.transformBeforeStore`](#fieldtransformbeforestore)\n    * [`field.transformBeforeRender`](#fieldtransformbeforerender)\n    * [`field.handlers`](#fieldhandlers)\n* [`strategy`](#propsstrategy)\n* [`asyncStrategy`](#propsasyncstrategy)\n* [`debounceInterval`](#propsdebounceinterval)\n* [`statuses`](#propsstatuses)\n* [**`handlers`**](#propshandlers)\n\n\n### `props.id`\n\n_Required: `no`_\u003cbr\u003e\n_Default: `'form'`_\n\n```js\ntype LayerId = string;\n```\n\nThe ID of the layer to avoid DOM ids collisions. This value is a namespace for generated DOM ids to ensure their uniqueness. Use it if you have few forms on a single page.\n\n\n### `props.data`\n\n_Required: `yes`_\u003cbr\u003e\n_Default: `—`_\n\n```js\ntype Data = { [attr: string]: any };\n```\n\nObject with the form data. Layer doesn't care about how you manage the state. You can use vanilla React, or Redux, or whatever as a state container. Just pack the data for the form  into single object and pass it to validation layer. Object can be flat or nested (see next section). [`immutable`](https://github.com/facebook/immutable-js) structures are supported (no need to call `toJS()`).\n\n\n### `props.fields`\n\n_Required: `yes`_\u003cbr\u003e\n_Default: `—`_\n\n```js\ntype Field = boolean | {\n  strategy?: Strategy,\n  asyncStrategy?: AsyncStrategy,\n  validate?: (value: Value, data: Data) =\u003e ValidationResults,\n  validateAsync?: (value: Value, data: Data) =\u003e Promise\u003cValidationResults\u003e,\n  debounceInterval?: number,\n  linkedFields?: Array\u003cstring | KeyPath\u003e,\n  filter?: (value: DomValue, data: Data) =\u003e boolean,\n  transformBeforeStore?: (value: DomValue, data: Data) =\u003e Value,\n  transformBeforeRender?: (value: Value, data: Data) =\u003e DomValue,\n  handlers?: Handlers,\n};\n\ntype Fields = { [attribute: string]: Field | Fields };\n```\n\nObject with form fields config. If a field doesn't have validations and other special handlers, then it must be set to `true` to let the layer know that this field exists, thus it can serve props for it.\n\n**Flat structures**\u003cbr\u003e\nIn case if `data` is flat, `fields` object must also be flat:\n\n```js\nconst data = {\n  username: 'alex',\n  email: 'alex@domain.com',\n};\n\nconst fields = {\n  username: true, // nothing special about this field, but letting layer know about it\n  email: { validate: email =\u003e !!email }, // config for `email` field\n};\n```\n\n**Nested structures**\u003cbr\u003e\nSometimes `data` is nested, `fields` object must replicate the shape of the `data` object:\n\n```js\nconst data = {\n  username: 'alex',\n  email: 'alex@domain.com',\n  creditCard: {\n    number: '1234567890',\n    owner: 'ALEX FEDOSEEV',\n  },\n};\n\nconst fields = {\n  username: true, // nothing special about this field, but letting layer know about it\n  email: { validate: email =\u003e !!email }, // config for `email` field\n  creditCard: { // replicating the shape\n    number: { validate: number =\u003e !!number },\n    owner: { validate: owner =\u003e !!owner },\n  },\n};\n```\n\n\n#### `field.strategy`\nCan be set on global or on the field level. See [`props.strategy`](#propsstrategy) for details.\n\n\n#### `field.asyncStrategy`\nCan be set on global or on the field level. See [`props.asyncStrategy`](#propsasyncstrategy) for details.\n\n\n#### `field.validate`\n\n_Required: `no`_\u003cbr\u003e\n_Default: `—`_\n\n```js\ntype Validate = (value: Value, data: Data) =\u003e ValidationResults;\n\ntype ValidationResults = boolean | {\n  valid: boolean,\n  message?: string,\n  status?: string,\n};\n```\n\nValidation function, which takes `value` and `data` object (the one that is passed to `\u003cValidationLayer /\u003e`). It can return either `boolean` or `Object`.\n\n`ValidationResults` as an object contains:\n* `valid`: boolean flag, tells if passed value is valid or not. Required.\n* `message`: usually a text string (or i18n id of a text string) which will show up in the view, when this result will be emitted. Actually it can be whatever you want, e.g. array of strings. Not required, if you don't use it.\n* `status`: suppose to identify css class, e.g. `success` or `failure` (those are defaults). Keep in mind that you can pass here any string to provide rich feedback to the user. E.g. when you validate credit card field, on successful validation instead of simple `success` status, you can pass `visa` / `mastercard` etc to display icon of the payment system. Not required, if you don't use it or fine with defaults. Also see [`props.statuses`](#propsstatuses).\n\n_NOTE: In case if you want to re-use validators somewhere else, `react-validation-layer` exposes `normalizeValidationResults` util, which takes result from the `field.validate` and normalizes it to `ValidationResults` object shape._\n\n```js\nimport { normalizeValidationResults } from 'react-validation-layer';\n\nconst normalizedValidationResults = normalizeValidationResults(field.validate(email));\n// =\u003e always object, e.g. `{ valid: true }`\n```\n\n\n#### `field.validateAsync`\n\n_Required: `no`_\u003cbr\u003e\n_Default: `—`_\n\n```js\ntype ValidateAsync = (value: Value, data: Data) =\u003e Promise\u003cValidationResults\u003e;\n```\n\nAsync validation function, which takes `value` and `data` object as well, but returns a `Promise`, which must be resolved with the same `ValidationResults` (see [`field.validate`](#fieldvalidate)).\n\n\n#### `field.debounceInterval`\nCan be set on global or on the field level. See [`props.debounceInterval`](#propsdebounceinterval) for details.\n\n\n\n#### `field.linkedFields`\n\n_Required: `no`_\u003cbr\u003e\n_Default: `—`_\n\n```js\ntype LinkedFields = Array\u003cstring | KeyPath\u003e;\n```\n\nSome fields might be dependent on each other and when the value of the one field is changed, another field might become valid or invalid, e.g. `password` \u0026 `passwordConfirmation`. Here you can define such relations.\n\nHow it works: when you define `linkedFields` for the field, you instruct the layer: \"When the value of this field is changed, also re-validate following fields\". Validators of the linked fields will receive `data` object with updated value of the parent field.\n\n\n#### `field.filter`\n\n_Required: `no`_\u003cbr\u003e\n_Default: `—`_\n\n```js\ntype Filter = (value: DomValue, data: Data) =\u003e boolean;\n```\n\nSometimes you want to filter out some user input. Filter function takes `value` and `data` object. If it returns `false`, then `handler.onChange` won't be triggered. It means that user's input will be ignored.\n\n_NOTE: `filter` doesn't filter empty strings._\n\n\n#### `field.transformBeforeStore`\n\n_Required: `no`_\u003cbr\u003e\n_Default: `—`_\n\n```js\ntype TransformBeforeStore = (value: DomValue, data: Data) =\u003e Value;\n```\n\n`event.target.value` is always a `string`. However, some attributes should be `number` by its nature or differentiate from the DOM representation in another way. To keep your data clean, you can provide transformation function, which will take string value from the DOM and transform it before send it to the data store. Here are few examples:\n\n* `price` field must be a `number` thus it can be safely used for calculations\n* in the view we want `creditCard` to be shown w/ spaces (eg `1234 5678 8765 4321`), but inside the data store it should be spaceless: `1234567887654321`.\n\n\n#### `field.transformBeforeRender`\n\n_Required: `no`_\u003cbr\u003e\n_Default: `—`_\n\n```js\ntype TransformBeforeRender = (value: Value, data: Data) =\u003e DomValue;\n```\n\nIn a certain way, this is opposite to previous method. If you want to format your number or represent credit card w/ spaces in the form field, you can make it happen using this hook.\n\n\n#### `field.handlers`\n\n_Required: `no`_\u003cbr\u003e\n_Default: `—`_\n\nCan be set on global or on the field level. This object is equal to [`props.handlers`](#propshandlers), except you can't define `onSubmit` handler here.\n\n\u003c!-- we're done with the `field` here --\u003e\n\n### `props.strategy`\n\n_Required: `no`_\u003cbr\u003e\n_Default: `'onFirstSuccessOrFirstBlur'`_\n\n```js\ntype Strategy =\n  | 'onFirstBlur'\n  | 'onFirstChange'\n  | 'onFirstSuccess'\n  | 'onFirstSuccessOrFirstBlur'\n  | 'onFirstSubmit'\n;\n```\n\nIn most cases validation feedback should be provided as soon as possible, but not too soon. The question comes down to when to start to provide the feedback. It really depends on context. Strategies below won't provide any feedback until the specific moment, e.g. the first blur from the field or the first successful validation. All you have to do is to pick the most suitable one for your context. To understand the behavior of each strategy, add the following prefix to its name: \"Start providing instant feedback on...\"\n\n#### `onFirstChange`\nValidation Layer emits results for the single field as user types. Note that first feedback will be provided only after first change in this field.\n\n#### `onFirstBlur`\nValidation Layer emits results on first blur. After first results were emitted—feedback is provided on every change in this field.\n\n#### `onFirstSuccess`\nValidation Layer emits results on first successful validation. After first results were emitted—feedback is provided on every change in this field.\n\n#### `onFirstSuccessOrFirstBlur` ✨\nValidation Layer emits first results immediately on successful validation or on the first blur. After first results were emitted—feedback is provided on every change in this field.\n\n#### `onFirstSubmit`\nValidation Layer emits first results only after first submission attempt. After this results for each field are emitted on every change in this field until validation layer will be reseted or remounted.\n\n_NOTE: After first submission of the form all fields are switched to `onFirstSubmit` strategy. It means that each field will receive feedback on every change in the field._\n\n\n### `props.asyncStrategy`\n\n_Required: `no`_\u003cbr\u003e\n_Default: `'onChange'`_\n\n```js\n\ntype AsyncStrategy =\n  | 'onBlur'\n  | 'onChange'\n;\n```\n\nSome validations can't be performed locally, e.g. on signup you want to validate if email from the input is available or already taken.\n\n#### `onChange` ✨\nThere are 2 common ways to provide async feedback: request the server on every change or only on blur event. The first one is better in terms of UX, but creates significant load, so your client might become slow or server might feel bad. The blur option doesn't have this problem (at least not that much), but UX is definitely not the best, b/c user have to blur away from the field to get the feedback.\n\nWhat can we do about it to have the best of both worlds? The answer is to debounce on change async validations. What does it mean and how does it work: when user types something in in the form field, no external requests are triggered. Instead, it's put on hold. While user types, we wait. Once he stopped and there was no activity in the certain period—request is triggered.\n\nValidation layer does this out of the box. Just enable `onChange` async strategy and you're all set 🤘\n\nAlso, it's a good UX to provide feedback in UI, when async validation is started. E.g. show a little spinner where you show your messages. Layer will let you know when to render it via [`layer.getAsyncStatusFor`](#layergetasyncstatusfor).\n\n#### `onBlur`\nThis strategy triggers async validation only on blur event. Use this if even debounced validations hurt your server (but don't forget that you can setup `debounceInterval`, it might help to reduce the load).\n\n-\n\nFew more things to keep in mind about async validations:\n* If sync strategy doesn't emit results -\u003e layer doesn't trigger async validation.\n* If sync validation fails -\u003e layer doesn't trigger async validation.\n* If sync validation succeeded and there is async validator for the field -\u003e results will be emitted only from async validation: when async validation is triggered, layer will notify about the start of async validation, and when results will be resolved -\u003e layer will serve them via props (as usual).\n* Layer does not perform any async validations on form submission as those validations will be performed on the server anyway within a single request (form submission). If server will reject submission and report errors, you can notify layer about it via callback, which accepts errors as argument (see [`props.handlers`](#propshandlers)).\n\n**N.B.** Single strategy can be set for all the fields globally (root props `strategy` \u0026 `asyncStrategy` of `\u003cValidationLayer /\u003e`), as well as on per-field basis (`field.strategy` \u0026 `field.asyncStrategy`). Field-level strategy has higher priority, so, if it's set, it will override global strategy for current field.\n\n-\n\n### `props.debounceInterval`\n\n_Required: `no`_\u003cbr\u003e\n_Default: `700`_\n\n```js\ntype DebounceInterval = number;\n```\n\nConfigure amount of time (in `ms`) that layer should wait after last user activity before debounced async validation will be invoked.\n\n\n### `props.statuses`\n\n_Required: `no`_\u003cbr\u003e\n_Default: `{ success: 'success', failure: 'failure' }`_\n\n```js\ntype Statuses = {\n  success: string,\n  failure: string,\n};\n```\n\nThese are default statuses for successful and failed validation results. Used, if no special values are provided from validators. Redefine it if you don't like the default ones.\n\n\n### `props.handlers`\n\n_Required: `yes`_\u003cbr\u003e\n_Default: `—`_\n\n```js\ntype Handlers = {\n  onChange?: (updatedData: UpdatedData) =\u003e void,\n  onBlur?: (updatedData: UpdatedData) =\u003e void,\n  onSubmit: (callbacks: OnSubmitCallbacks) =\u003e void,\n};\n```\n\nTell the layer how to handle data updates and form submission. `onChange` and `onBlur` can be defined on the field level, so if every field has its own `onChange` method, on the props level it's not required. `onBlur` is always optional, but check out its section below for gotchas. `onSubmit` can be set only here and it is required.\n\n#### `handlers.onChange`\nThis is the method, which you must use to update form state in your app. It receive one argument from validation layer:\n\n```js\ntype OnChange = (updatedData: UpdatedData) =\u003e void;\n\ntype UpdatedData = {\n  // Attribute that was updated\n  attr: string,\n\n  // If attribute is nested, this is key path to it\n  keyPath: Array\u003cstring\u003e,\n\n  // Next value of attribute\n  value: Value,\n\n  // Value of `checked` DOM attribute\n  checked: boolean,\n\n  // Original data object (note: doesn't contain updated value)\n  data: Data,\n\n  // Original DOM event\n  event: SyntheticInputEvent,\n|};\n```\n\nIf `transformBeforeStore` method is defined for this field, then the `updatedData.value` will be the returned value from this method, otherwise it's just a `string` from the DOM.\n\n**NB** `onChange` handler must put in state exactly the same `value`, that was passed to it from the layer, as layer uses it for validation.\n\n#### `handlers.onBlur`\nUsually you don't need this. So if it's the case, just ignore this handler. But if you actually want to do some stuff on blur event—don't override layer's handler in representation by putting `onBlur` prop on DOM input field directly, but provide it to the layer here and it will trigger it for you (with the same `UpdatedData` object as argument). Otherwise layer won't be able to handle blur events correctly. If you still want to redefine it from the representation, then see [`layer.notifyOnBlur`](#layernotifyonblur).\n\n#### `handlers.onSubmit`\nThis method will be triggered on form submission if all fields of the form are passed validation. It receives object with 2 callbacks as argument:\n\n```js\ntype OnSubmit = (callbacks: OnSubmitCallbacks) =\u003e void;\n\ntype OnSubmitCallbacks = {\n  onSuccess: () =\u003e void,\n  onFailure: (errors: {}) =\u003e void,\n};\n```\n\nInvoke `onSuccess` after successful response from the server. It will reset internal validation layer state to its initial state.\n\nIn case if something went wrong and your API responded with errors, invoke `onFailure` callback with these errors. Layer will pass them to representation in general way. Error object must replicate the shape of the `data` / `fields` objects, e.g.:\n\n```js\nconst externalErrors = {\n  email: 'Bad email',\n  creditCard: {\n    number: 'Bad credit card number',\n  },\n};\n```\n\n## Rendering\nValidation layer requires `children` to be a function. This function receives single argument `layer`: an interface to the data from validation layer. Here is how it looks like:\n\n```js\n\u003cValidationLayer {...}\u003e\n  {layer =\u003e (\n    \u003cform onSubmit={layer.handleSubmit}\u003e\n      \u003cinput type=\"text\" {...layer.getPropsFor('email')} /\u003e\n      \u003cinput type=\"text\" {...layer.getPropsFor('password')} /\u003e\n      \u003cbutton {...layer.getSubmitButtonProps()}\u003e\n        Submit\n      \u003c/button\u003e\n    \u003c/form\u003e\n  )}\n\u003c/ValidationLayer\u003e\n```\n\nAnd here is what you can get:\n\n* [`getPropsFor`](#layergetpropsfor)\n* [`getCheckboxPropsFor`](#layergetcheckboxpropsfor)\n* [`getRadioButtonPropsFor`](#layergetradiobuttonpropsfor)\n* [`getCustomPropsFor`](#layergetcustompropsfor)\n* [`getSubmitButtonProps`](#layergetsubmitbuttonprops)\n* [`getValidityFor`](#layergetvalidityfor)\n* [`isSuccessFor`](#layerissuccessfor)\n* [`isFailureFor`](#layerisfailurefor)\n* [`getStatusFor`](#layergetstatusfor)\n* [`getMessageFor`](#layergetmessagefor)\n* [`getAsyncStatusFor`](#layergetasyncstatusfor)\n* [`getSubmissionStatus`](#layergetsubmissionstatus)\n* [`getDomIdFor`](#layergetdomidfor)\n* [`getFieldIdFor`](#layergetfieldidfor)\n* [`notifyOnChange`](#layernotifyonchange)\n* [`notifyOnBlur`](#layernotifyonblur)\n* [`handleSubmit`](#layerhandlesubmit)\n* [`resetState`](#layerresetstate)\n\n**Providing paths to field data**\u003cbr\u003e\nUsually you _`getSomething`_ for specific field. In case if your `fields` object is flat, just pass attribute name to getter:\n\n```js\nconst fields = { email: true };\n\nlayer.getStatusFor('email') // \u003c- string\n```\n\nIf you deal with nested structures and want to _`getSomething`_ for the field, that's nested more than 1 level deep, provide key path to it:\n\n```js\nconst fields = { user: { email: true } };\n\nlayer.getStatusFor(['user', 'email']) // \u003c- array of strings\n```\n\n\n### `layer.getPropsFor`\n\n```js\ntype GetPropsFor = (attr: string | KeyPath) =\u003e FieldDomProps;\n\n\u003cinput type=\"text\" {...layer.getPropsFor('email')} /\u003e\n```\n\nReturns props for general input DOM element (e.g. text input). It contains props like `value`, `onChange` etc. Apply it via spread operator.\n\n\n### `layer.getCheckboxPropsFor`\n\n```js\ntype GetCheckboxPropsFor = (attr: string | KeyPath) =\u003e FieldDomPropsWithChecked;\n\n\u003cinput type=\"checkbox\" {...layer.getCheckboxPropsFor('subscribe')} /\u003e\n```\n\nReturns props for checkbox. Same as `getPropsFor`, but with `checked` attribute.\n\n\n### `layer.getRadioButtonPropsFor`\n\n```js\ntype GetRadioButtonPropsFor = (attr: string | KeyPath, value: string) =\u003e FieldDomPropsWithChecked;\n\n\u003cinput type=\"radio\" {...layer.getRadioButtonPropsFor('paymentMethod', 'card')} /\u003e\n\u003cinput type=\"radio\" {...layer.getRadioButtonPropsFor('paymentMethod', 'paypal')} /\u003e\n\u003cinput type=\"radio\" {...layer.getRadioButtonPropsFor('paymentMethod', 'cash')} /\u003e\n```\n\nWhen you render radio buttons, you must render one radio button for each possible value of the attribute. So, in addition to attribute, pass `value` as a second argument to get props for radio button.\n\n\n### `layer.getCustomPropsFor`\n\n```js\ntype GetCustomPropsFor = (attr: string | KeyPath, options: Options) =\u003e FieldDomProps | FieldDomPropsWithChecked;\n\ntype Options = {\n  value?: string,\n  disabled?: ?boolean,\n  getChecked?: (value: string) =\u003e boolean,\n};\n```\n\nSometimes you want to do fancy stuff in UI and involve uncommon logic. If you can't achieve what you want with standard getters, here is constructor for you. It takes attribute name/key path + object with options (all keys are optional):\n\n* `value`: if provided, used to build radio button DOM id (**NOT as `value` DOM attribute!**). Should be a `string`. Use it only if you render radio button.\n* `disabled`: if provided, will be used for `disabled` DOM attribute. Should be `boolean`.\n* `getChecked`: if provided, will be used to get `checked` DOM attribute. It must be a function, which takes 1 argument: DOM `value` of the field, and returns `boolean`.\n\n\n### `layer.getSubmitButtonProps`\n\n```js\ntype GetSubmitButtonProps = () =\u003e SubmitButtonDomProps;\n\n\u003cbutton {...layer.getSubmitButtonProps()} /\u003e\n```\n\nReturns props for submit button. Its only purpose is to disable button on form submission to prevent multiple submissions.\n\n\n### `layer.getValidityFor`\n\n```js\ntype GetValidityFor = (attr: string | KeyPath) =\u003e boolean | null;\n```\n\nReturns validity for the field. Keep in mind that in case if layer, according to strategy, isn't ready to provide feedback yet, it will return `null`.\n\n\n### `layer.getStatusFor`\n\n```js\ntype GetStatusFor = (attr: string | KeyPath) =\u003e string;\n```\n\nReturns `status` for a field. The one that is passed (or not) via validation results.\n\nKeep in mind that in case if layer, according to strategy, isn't ready to provide a feedback yet, it will return `null`.\n\nAlso, in case if filed doesn't have a value, but still valid, status still is set to `null`. This is because the main use-case for `status` is CSS class name, but we don't want to paint everything green if field is empty.\n\n\n### `layer.isSuccessFor`\n\n```js\ntype IsSuccessFor = (attr: string | KeyPath) =\u003e boolean;\n```\n\nBasically, it's a shorthand to get boolean result if success status is emitted for a field (instead of using comparison operators with [`layer.getStatusFor`](#layergetstatusfor)). Keep in mind, that emitting behavior is consistent with the [`layer.getStatusFor`](#layergetstatusfor) method: it will return `false` if, according to strategy, layer isn't ready to emit `status` or value is not present (even if it's a valid case for a field). To get the validity, use [`layer.getValidityFor`](#layergetvalidityfor)\n\n\n### `layer.isFailureFor`\n\n```js\ntype IsFailureFor = (attr: string | KeyPath) =\u003e boolean;\n```\n\nSame as `layer.isSuccessFor`, but for the failure status.\n\n\n### `layer.getMessageFor`\n\n```js\ntype GetMessageFor = (attr: string | KeyPath) =\u003e string;\n```\n\nReturns `message` for the field. The one that is passed (or not) via validation results. Keep in mind that in case if layer, according to strategy, isn't ready to provide feedback yet, it will return `null`.\n\n\n### `layer.getAsyncStatusFor`\n\n```js\ntype GetAsyncStatusFor = (attr: string | KeyPath) =\u003e string;\n```\n\nReturns `true` if async validation is in process for the field.\n\n\n### `layer.getSubmissionStatus`\n\n```js\ntype GetSubmissionStatus = () =\u003e boolean;\n```\n\nReturns `true` if form is submitting.\n\n\n### `layer.getDomIdFor`\n\n```js\ntype GetDomIdFor = (attr: string | KeyPath, value?: string) =\u003e string;\n\n\u003clabel htmlFor={layer.getDomIdFor('email')}\u003e\n  Email\n\u003c/label\u003e\n\u003cinput type=\"text\" {...layer.getPropsFor('email')} /\u003e\n```\n\nReturns `id` attribute of the DOM element for the field. You might need it for a various reasons, the most commonly used one is to provide DOM field `id` to `\u003clabel /\u003e` for the field. Don't forget to provide `value` as second argument if you need `id` for radio button.\n\n\n### `layer.getFieldIdFor`\n\n```js\ntype GetFieldIdFor = (attr: string | KeyPath) =\u003e string;\n```\n\nReturns internal `id` of the field. You might need it for custom notifiers (see below).\n\n\n### `layer.notifyOnChange`\n\n```js\ntype NotifyOnChange = (fieldId: FieldId, value: Value) =\u003e void;\n\nlayer.notifyOnChange(\n  layer.getFieldIdFor('startDate'),\n  nextStartDate,\n);\n```\n\nIf you do some fancy stuff with the field (e.g. update its value via JS / third-party tool, e.g. date picker), then use this method to notify layer about the change, so it can perform validations.\n\n\n### `layer.notifyOnBlur`\n\n```js\ntype NotifyOnBlur = (\n  fieldId: FieldId,\n  value: Value,\n  event: SyntheticInputEvent,\n) =\u003e void;\n\nlayer.notifyOnBlur(\n  layer.getFieldIdFor('startDate'),\n  startDate,\n  event,\n);\n```\n\nSame as `layer.notifyOnChange`, but to notify layer about `blur` events. You might need it in case if you've redefined `onBlur` handler of the DOM node, but still want to notify layer about event, so it can use this information to figure out correct strategy. Don't forget to pass `event` as last arg.\n\n\n### `layer.handleSubmit`\n\n```js\n\u003cform onSubmit={layer.handleSubmit} /\u003e\n```\n\n^ That's all you need to do with it.\n\n\n### `layer.resetState`\n\n```js\n\u003cbutton onClick={layer.resetState}\u003e\n  Reset\n\u003c/button\u003e\n```\n\nResets validation layer internal state.\n\n\n## Lifecycles\nHere is the quick overview what's happening on data updates and form submission under the hood.\n\n### Value update\n\n* `onChange` / `onBlur` handler is triggered from the DOM\n* `field.filter` is triggered `?-\u003e`\n    * if `false` is returned `-\u003e` we're done\n    * if `true` is returned `-\u003e` continue\n* `field.transformBeforeStore` is triggered (if it's defined)\n* `field.onChange` (or `props.onChange`) is triggered\n* sync validation is triggered (if any) `?-\u003e`\n    * if no results emitted `-\u003e` updating the state and we're done\n    * if failed results emitted `-\u003e` updating the state and we're done\n    * if success results emitted `?-\u003e`\n        * if there is no async validation `-\u003e` updating the state and we're done\n        * if there is async validation `-\u003e` updating the state with processing status and trigger async validation, when results are resolved `-\u003e` updating the state and we're done.\n\n### Form submission\n\n* `layer.handleSubmit` is triggered from the DOM\n* Validation layer performs sync validation of all fields (no async validations performed) `?-\u003e`\n    * if at least one field is invalid `-\u003e` updating the state with the errors and we're done\n    * if all fields are valid `-\u003e` triggering `handlers.onSubmit` and pass object with callbacks `?-\u003e`\n        * `callback.onSuccess` should be triggered when form is successfully submitted `-\u003e` resetting the layer to initial state and we're done\n        * `callback.onFailure` should be triggered when something gone wrong, i.e. API returned errors, which should be passed to `callback.onFailure` `-\u003e` updating the state with the errors and we're done.\n\n\n## WIPs \u0026 TODOs\n\nThose will be figured out (sooner or later), upvote them if you need them :+1:\n\n* [ ] Collections handling, e.g. arrays of entities ([#12](https://github.com/shakacode/react-validation-layer/issues/12))\n* [ ] Refs handling, e.g. focus on first invalid input after submission ([#13](https://github.com/shakacode/react-validation-layer/issues/13))\n\nSee [issues](/issues) \u0026 [pull requests](/pulls) for more details.\n\n## License\n\nIt's MIT.\n","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fshakacode%2Freact-validation-layer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fshakacode%2Freact-validation-layer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fshakacode%2Freact-validation-layer/lists"}