{"id":17996686,"url":"https://github.com/busypeoples/revalidation","last_synced_at":"2025-04-07T11:11:14.601Z","repository":{"id":85464347,"uuid":"93956931","full_name":"busypeoples/revalidation","owner":"busypeoples","description":"Higher Order Component for Validating Forms in React","archived":false,"fork":false,"pushed_at":"2019-05-08T18:07:37.000Z","size":1328,"stargazers_count":378,"open_issues_count":10,"forks_count":20,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-03-29T04:44:41.240Z","etag":null,"topics":["form","react","validations"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/busypeoples.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-06-10T18:05:29.000Z","updated_at":"2025-01-22T15:47:38.000Z","dependencies_parsed_at":"2023-08-01T15:46:40.489Z","dependency_job_id":null,"html_url":"https://github.com/busypeoples/revalidation","commit_stats":null,"previous_names":["25th-floor/revalidation"],"tags_count":14,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/busypeoples%2Frevalidation","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/busypeoples%2Frevalidation/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/busypeoples%2Frevalidation/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/busypeoples%2Frevalidation/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/busypeoples","download_url":"https://codeload.github.com/busypeoples/revalidation/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247312077,"owners_count":20918344,"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":["form","react","validations"],"created_at":"2024-10-29T21:15:32.297Z","updated_at":"2025-04-07T11:11:14.576Z","avatar_url":"https://github.com/busypeoples.png","language":"JavaScript","readme":"# Revalidation\n\n### Higher Order Component for Forms in React\n\n\n__Revalidation__ lets you write your forms as __stateless function components__, taking care of managing the local form state\nas well as the validation. Revalidation also works with classes and will support other React-like libraries like __Preact__ or __Inferno__\nin the future.\n\n### Use Case\nForm handling sounds trivial sometimes, but let’s just take a second to think about what is involved in the process.\nWe need to define form fields, we need to validate the fields,\nwe also might need to display errors according to the fact if the input validates,\nfurthermore we need to figure out if the validation is instant or only after clicking\non a submit button and so on and so forth.\n\n### Why Revalidation?\nThere are a number of solutions already available in __React__ land, each with there own approach on how to tackle aforementioned problems.\n__Revalidation__ is another approach on taming the problems that come with forms by only doing two things: managing the\nlocal form component state and validating against a defined set of rules. There is no real configuration and very\nlittle is hidden away from user land. This approach has pros and cons obviously. The benefit we gain, but declaring an initial state\nand a set of rules is that we can reuse parts of the spec and compose those specs to bigger specs. The downside is that\nRevalidation doesn't abstract away the form handling itself. The only configurations available are `validateSingle` and\n`validateOnChange`, while the first enables to define if the predicates functions are against all fields or only that one updated,\nthe latter enables to turn dynamic validation on and off all together. This is it. Everything is up to the form implementer.\n\n__Revalidation__ enhances the wrapped Component by passing a `revalidation` prop containing a number of properties and functions\nto manage the state. There are no automatic field updates, validations or _onsubmit_ actions, Revalidation doesn't know how\nthe form is implemented or how it should handle user interactions.\n\nLet's see an example to get a better idea on how this could work.\nFor example we would like to define a number of validation rules for two inputs, _name_ and _random_.\nMore often that not, inside an `onChange(name, value)` f.e, we might start to hard code some rules and verify them against\nthe provided input:\n\n```js\n\nonChange(name, value) {\n  if (name === 'lastName') {\n    if (hasCapitalLetter(lastName)) {\n      // then do something\n    }\n  }  \n  // etc...\n}\n\n```\n\nThis example might be exaggerated but you get the idea.\nRevalidation takes care of running your predicate functions against defined field inputs, enabling to decouple the actual input from the predicates.\n\n\n```javascript\nconst validationRules = {\n  name: [\n    [ isGreaterThan(5),\n      `Minimum Name length of 6 is required.`\n    ],\n  ],\n  random: [\n    [ isGreaterThan(7), 'Minimum Random length of 8 is required.' ],\n    [ hasCapitalLetter, 'Random should contain at least one uppercase letter.' ],\n  ]\n}\n```\nAnd imagine this is our input data.\n\n```javascript\nconst inputData = { name: 'abcdef', random: 'z'}\n```\n\nWe would like to have a result that displays any possible errors.\n\nCalling validate `validate({inputData, validationRules)`\nshould return\n```javascript\n{name: true,\n random: [\n    'Minimum Random length of 8 is required.',\n    'Random should contain at least one uppercase letter.'\n]}\n```\n\nRevalidate does exactly that, by defining an initial state and the validation rules it takes care of updating and validating\nany React Form Component. Revalidate also doesn't know how your form is built or if it is even a form for that matter.\nThis also means, a form library can be built on top Revalidation, making it a sort of meta form library.\n\n\n### Getting started\n\nInstall revalidation via npm or yarn.\n\n\n```\nnpm install --save revalidation\n```\n\n### Example\n\nWe might have a stateless function component that receives a prop ___form___, which include the needed field values.\n\n```javascript\n\nimport React, {Component} from 'react'\n\nconst Form = ({ form, onSubmit }) =\u003e\n  (\n    \u003cdiv className='form'\u003e\n      \u003cdiv className='formGroup'\u003e\n        \u003clabel\u003eName\u003c/label\u003e\n        \u003cinput\n          type='text'\n          value={form.name}\n        /\u003e\n      \u003c/div\u003e\n      \u003cdiv className='formGroup'\u003e\n        \u003clabel\u003eRandom\u003c/label\u003e\n        \u003cinput\n          type='text'\n          value={form.random}\n        /\u003e\n      \u003c/div\u003e\n      \u003cbutton onClick={() =\u003e onSubmit(form)}\u003eSubmit\u003c/button\u003e\n    \u003c/div\u003e\n  )\n\n```\n\nNext we might have a defined set of rules that we need to validate for given input values.\n\n```javascript\nconst validationRules = {\n  name: [\n    [isNotEmpty, 'Name should not be  empty.']\n  ],\n  random: [\n    [isLengthGreaterThan(7), 'Minimum Random length of 8 is required.'],\n    [hasCapitalLetter, 'Random should contain at least one uppercase letter.'],\n  ]\n}\n\n```\n\nFurther more we know about the inital form state, which could be empty field values.\n\n```javascript\nconst initialState = {password: '', random: ''}\n```\n\nNow that we have everything in place, we import Revalidation.\n\n```js\nimport Revalidation from 'revalidation'\n```\nRevalidation only needs the Component and returns a Higher Order Component accepting the following props:\n\n- __`initialState`__ *(Object)*\n\n- __`rules`__ *(Object)*\n\n- __`validateSingle`__ *(Boolean)*\n\n- __`validateOnChange`__: *(Boolean|Function)*\n\n- __`asyncErrors`__ *(Object)*\n\n- __`updateForm`__ *(Object)*\n\n\n```js\n\nconst enhancedForm = revalidation(Form)\n\n// inside render f.e.\n\n\u003cEnhancedForm\n  onSubmit={this.onSubmit} // pass any additional props down...\n  initialState={initialState}\n  rules={validationRules}\n  validateSingle={true}\n  validateOnChange={true}\n  {/*\n    alternatively pass in a function, i.e. enable validationOnChange after a submit.\n    validateOnChange={(submitted) =\u003e submitted}\n  */}\n/\u003e\n\n```\n\nThis enables us to rewrite our Form component, which accepts a ___revalidation___ prop now.\n\n```js\n\nconst createErrorMessage = (errorMsgs) =\u003e\n  isValid(errorMsgs) ? null : \u003cdiv className='error'\u003e{head(errorMsgs)}\u003c/div\u003e\n\nconst getValue = e =\u003e e.target.value\n\nconst Form = ({ revalidation : {form, onChange, updateState, valid, errors = {}, onSubmit}, onSubmit: onSubmitCb }) =\u003e\n  (\n  \u003cdiv className='form'\u003e\n    \u003cdiv className='formGroup'\u003e\n      \u003clabel\u003eName\u003c/label\u003e\n      \u003cinput\n        type='text'\n        className={isValid(errors.name) ? '' : 'error'}\n        value={form.name}\n        onChange={compose(onChange('name'), getValue)}\n      /\u003e\n      \u003cdiv className='errorPlaceholder'\u003e{ createErrorMessage(errors.name) }\u003c/div\u003e\n    \u003c/div\u003e\n    \u003cdiv className='formGroup'\u003e\n      \u003clabel\u003eRandom\u003c/label\u003e\n      \u003cinput\n        type='text'\n        className={isValid(errors.random) ? '' : 'error'}\n        value={form.random}\n        onChange={compose(onChange('random'), getValue)}\n      /\u003e\n      \u003cdiv className='errorPlaceholder'\u003e{ createErrorMessage(errors.random) }\u003c/div\u003e\n    \u003c/div\u003e\n    \u003cbutton onClick={() =\u003e onSubmit(onSubmitCb)}\u003eSubmit\u003c/button\u003e\n  \u003c/div\u003e\n  )\n\nexport default revalidation(Form)\n```\n\nrevalidtion returns an object containing:\n- __form__: form values\n- __onChange__: a function expecting form name and value, additionally one can specify if the value and/or the validation should be updated and also accepts a callback function that will be run after an update has occurred. i.e.\n\n```js\nonChange('name', 'foo')\n// or\nonChange('name', 'foo', [UPDATE_FIELD])\n// or\nonChange('name', 'foo', null, ({valid, form}) =\u003e valid ? submitCb(form) : null )\n```\n\n- __updateState__: a function expecting all the form values, f.e. Useful when wanting to reset the form. Depending on the setting either a validation will occur or not.\n\n\n```js\n \u003cbutton onClick={() =\u003e updateState({ name: '', random: '' })}\u003eReset\u003c/button\u003e\n```\n\n- __valid__: calculated validation state, f.e. initially disabling the submit button when a form is rendered.\n- __submitted__: set to true once the form has been submitted.\n- __errors__: the errors object containing an array for every form field.\n- __onSubmit__: validates all fields at once, also accepts a callback function that will be called after the a validation state has been calculated. The callback function receives the current state including the valid state.\n\n```js\n\u003cbutton\n  onClick={() =\u003e onSubmit(({form, valid}) =\u003e valid ? submitCb(form) : console.log('something went wrong!'))}\n\u003e\n  Submit\n\u003c/button\u003e\n```\n\n- __updateErrors__: Enables to update any errors.\n\n\n- __updateAsyncErrors__: Enables to update any asynchronous errors. Useful when working with asynchronous validations.\nPass the `updateAsyncErrors` to a callback, once the validation is finished set the result manually.\n\n```js\n\u003cbutton\n  onClick={() =\u003e onSubmit(({form, valid}) =\u003e valid ? submitCb(form, updateAsyncErrors) : console.log('something went wrong!'))}\u003eSubmit\n\u003c/button\u003e\n\n// use in another Component...\nclass HigherUpComponent extends React.Component {\n  onSubmit = (formValues, updateAsyncErrors) =\u003e {\n    setTimeout(() =\u003e {\n      // something went wrong...\n      updateAsyncErrors({ name: ['Username is not available'] })\n    }, 1000)\n  }\n\n  render() {\n    {/* ... */}\n  }\n}\n```\n\n- __settings__: access the current settings: `{ validateOnChange: true, validateSingle: true }`\n\nAdditionally revalidation offers a number of helper functions to quickly update any values or validations.\n\n- __debounce__: a helper function for triggering asynchronous validations. The passed in asynchronous validation can be debounced by a specified time. i.e. 1000 ms.\n\n```js\n\u003cinput\n  type=\"text\"\n  value={form.name}\n  onChange={debounce.name(usernameExists, 1000)}\n/\u003e\n```\n\n- __updateValue__: update a specific field value. Important to note that no validation will run. Use _updateValueAndValidate_ if you need to update and validate of field. A name attribute must be defined on the element for _updateValue_ to update the value.\n\n```js\n  \u003cinput\n    type='text'\n    className={isValid(errors.random) ? '' : 'error'}\n    name='random'\n    value={form.random}\n    onChange={updateValue}\n  /\u003e\n```\n\n- __validateValue__: validates a specific field. Useful when validation should happen after an *onBlur* i.e.\nA name attribute must be defined on the element for _validateValue_ to validate the value.\n\n```js\n  \u003cinput\n    type='text'\n    className={isValid(errors.random) ? '' : 'error'}\n    name='random'\n    onBlur={validateValue}\n    value={form.random}\n    onChange={updateValue}\n  /\u003e\n```\n\n- __updateValueAndValidate__: Updates and validates the value for the specified element.\nA name attribute must be defined on the element for _updateValueAndValidate_ to update the value.\n\n```js\n  \u003cinput\n    type='text'\n    className={isValid(errors.random) ? '' : 'error'}\n    name='random'\n    onBlur={validateValue}\n    value={form.random}\n    onChange={updateValue}\n  /\u003e\n```\n\n\nWhere and how to display the errors and when and how to validate is responsibility of the form not Revalidation.\nAnother aspect is that the form props can be updated when needed.\n\n__NOTE:__ `updateForm` should be used carefully and only when needed. Make sure to reset or remove `updateForm` after\napplying the new form values.\n\n\n```javascript\n\u003cForm\n  onSubmit={this.onSubmit}\n  updateForm={{name: 'foobar', random: ''}}\n/\u003e\n```\n\nEither define an initial state or use form props to define an actual form state. Revalidation will check for props first\nand then fallback to the initial state when none is found.\n\n__Revalidation__ also enables to pass in asynchronous error messages via the `asyncErrors` prop. As side effects are run outside of Revalidation itself, any error messages (from a dynamic validation or after submitting to a server and receiving errors) can be passed back into Revalidation.\n\n```js\n\n// i.e. userNameExists is a function returning a promise and sends a request to validate if the username is available.\n\n\u003cEnhancedSubmitForm\n  onSubmit={this.onSubmit}\n  rules={validationRules}\n  initialState={initialState}\n  asyncErrors={{name: ['Not available.']}}\n  userNameExists={this.usernameExists}\n  validateSingle={true}\n  validateOnChange={true}\n/\u003e\n\n```\n\n__NOTE:__ A sensible approach with asynchronous validation functions is useful, Revalidation will not run any effects against\nan input field. Needed consideration include: when to run the side effects\n(dynamically or on submit) and how often to trigger an async validation (immediately on every change or debounced)\n\n\nMore: Revalidation also works with deep nested data structure (see the deep nested data example)\n\ncheck the [example](https://github.com/25th-floor/revalidation/tree/master/example) for more detailed insight into how to build more advanced forms, f.e. validating dependent fields.\n\nClone the repository go to the examples folder and run the following commands:\n\n```js\nyarn install\nnpm start.\n```\n\n### Demo\nCheck the live [demo](http://revalidation.oss.25th-floor.com/example/demo/)\n\n### Further Information\n\nFor a deeper understanding of the underlying ideas and concepts:\n\n[Form Validation As A Higher Order Component Pt.1](https://medium.com/javascript-inside/form-validation-as-a-higher-order-component-pt-1-83ac8fd6c1f0)\n\n[Form Validation As A Higher Order Component Pt.2](https://medium.com/javascript-inside/form-validation-as-a-higher-order-component-pt-2-1edb7881870d)\n\n\n### Credits\nWritten by [A.Sharif](https://twitter.com/sharifsbeat)\n\nOriginal idea and support by [Stefan Oestreicher](https://twitter.com/thinkfunctional)\n\nVery special thanks to [Alex Booker](https://twitter.com/bookercodes) for providing input on the API and creating use cases.\n\n#### More\n__Revalidation__ is under development.\n\nThe underlying synchronous validation is handled via [__Spected__](https://github.com/25th-floor/spected)\n\n#### Documentation\n[API](docs/API.md)\n\n[FAQ](docs/FAQ.md)\n\n### License\n\nMIT\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbusypeoples%2Frevalidation","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbusypeoples%2Frevalidation","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbusypeoples%2Frevalidation/lists"}