Skip to content

Commit

Permalink
Merge 70adc49 into 9fbcdb3
Browse files Browse the repository at this point in the history
  • Loading branch information
j-a-y-h committed Aug 25, 2019
2 parents 9fbcdb3 + 70adc49 commit e6c00da
Show file tree
Hide file tree
Showing 4 changed files with 169 additions and 14 deletions.
39 changes: 26 additions & 13 deletions README.md
Expand Up @@ -2,6 +2,9 @@

<div align="center"><p align="center">

[![travis](https://travis-ci.com/j-a-y-h/react-uniformed.svg?branch=develop)](https://travis-ci.com/j-a-y-h/react-uniformed.svg?branch=develop)
[![Coverage Status](https://coveralls.io/repos/github/j-a-y-h/react-uniformed/badge.svg?branch=coveralls)](https://coveralls.io/github/j-a-y-h/react-uniformed?branch=coveralls)
[![license](https://badgen.now.sh/badge/license/MIT)](./LICENSE)
[![npm](https://badgen.net/bundlephobia/minzip/react-uniformed)](https://badgen.net/bundlephobia/minzip/react-uniformed)

</p></div>
Expand All @@ -12,17 +15,22 @@

##### Overview
* ‍️💆🏾‍♂️ Simple API
* 🐐 Lightweight / Fast / Scalable
* 🐐 Fast / Scalable
* 🙅🏻‍♀️ Zero dependencies
* 💌 < 4k gzipped
* 📜 HTML standard validation
* 🚀 Controlled & Uncontrolled inputs support

##### References
* [Validation](#validation)
* [Performance](#performance)
* [API](https://github.com/j-a-y-h/react-uniformed/blob/documentation/docs/API.md)

## Install

NPM
```shell
npm install --save react-uniformed
npm install react-uniformed
```
Yarn
```shell
Expand All @@ -37,8 +45,10 @@ import {useForm, useSettersAsEventHandler} from "react-uniformed";
const { setValue, values, submit } = useForm({
onSubmit: data => console.log(JSON.stringify(data)),
});

// compose your event handlers using useSettersAsEventHandler
const handleChange = useSettersAsEventHandler(setValue);

return (
{/* the submit function is only called after the form passes validation */}
<form onSubmit={submit}>
Expand Down Expand Up @@ -77,10 +87,12 @@ const validators = useConstraints({
min: [Date.now(), "Date must be today or later"]
}
});

const { setValue, validateByName, errors } = useForm({
validators,
onSubmit: data => console.log(JSON.stringify(data)),
});

// validate on change with the following code
// const handleChange = useSettersAsEventHandler(setValue, validateByName);
// or validate on blur
Expand All @@ -104,24 +116,25 @@ const changeRef = useSettersAsRefEventHandler(setValue);
```javascript
const {validateByName, errors} = useForm({
validators: {
// name won't be valid because validators must return empty string for valid values
name: (value) => "name will never be valid",
email: (value) => value ? "" : "email is required"
// validators must return empty string for valid value
name: (value) => value ? "" : "email is required",
},
});

// useConstraints supports mixing validators and constraints
const validator = useConstraints({
const validators = useConstraints({
name: (value) => "name still won't be valid",
email: { required: true }
})
email: { required: true },
});

// when used with useSettersAsEventHandler the validator
// will call the validation that matches the current input element's name
const handleBlur = useSettersAsEventHandler(validateByName);
```
If you prefer to validate in one function, then you can do that as well
```javascript
const {
// validateByName will call the validate function on each call
// note: validateByName will call the validate function on each call
// but the error will be the one with the corresponding name
validateByName,
// validate is available with both a validation map and a validation function
Expand All @@ -130,14 +143,14 @@ const {
validators(values) {
const errors = {name: "name will never be valid", email: ""};
if (!values.email) {
errors.email = "email is required"
errors.email = "email is required";
}
return errors;
},
});
```
## Build Forms Without `useForm`
It should be noted that `useForm` is just one layer of abstraction used to simplify the form building process. If you need more granular control and orchestration of your form then you should avoid using `useForm` in favor of the form modules like `useFields`, `useTouch`, `useValidation`, and `useSubmission`. The following is a basic implementation of `useForm` that you can use to compose your form to your needs.
## More Hooks
It should be noted that `useForm` is just one layer of abstraction used to simplify the form building process. If you need more granular control and orchestration of your form, then you should avoid using `useForm` in favor of other form hooks like `useFields`, `useTouch`, `useValidation`, and `useSubmission`. The following is a basic implementation of `useForm` that you can use to compose your forms.
```javascript
import {useCallback} from "react";
import {
Expand Down Expand Up @@ -169,4 +182,4 @@ function useForm() {
// Guards against submissions until all values are valid
const { submit } = useSubmission({ onSubmit, validator });
}
```
```
102 changes: 102 additions & 0 deletions docs/API.md
@@ -0,0 +1,102 @@
## Functions

<dl>
<dt><a href="#useConstraints">useConstraints(rules)</a> ⇒</dt>
<dd><p>A declarative way of validating inputs based upon HTML 5 constraints</p>
</dd>
<dt><a href="#useValidation">useValidation(validator, expectedFields)</a> ⇒</dt>
<dd><p>A hook for performing validation.</p>
</dd>
</dl>

<a name="useConstraints"></a>

## useConstraints(rules) ⇒
A declarative way of validating inputs based upon HTML 5 constraints

**Kind**: global function
**Returns**: maps the rules to an object map with the value
being a function that accepts value as the only argument.

| Param | Description |
| --- | --- |
| rules | an object mapping that consist of HTML5ValidatorRules as value or validator function that accepts value as the only argument. |

**Example**
```js
// BASIC
const validator = useConstraints({
firstName: { required: true, minLength: 5, maxLength: 6 },
lastName: { required: true, maxLength: 100 },
age: { type: "number", min: 18, max: 99 },
location: { required: true, pattern: /(europe|africa)/},
email: { required: true, type: "email" },
website: { required: true, type: "url" }
})
// ADVANCED
const validator = useConstraints({
// use min, max on date type
startDate: { type: "date", min: Date.now() },
// custom message
name: {
required: "name is required",
maxLength: [55, "name must be under 55 characters"]
},
})
// BIND CONSTRAINTS TO VALUES
const validator = useConstraints((values) => ({
startDate: { type: "date", min: Date.now() },
// ensure that the end date is always greater than the start date
endDate: {
type: "date",
min: [values.startDate, "end date must be greater than start date"]
},
}))
// note: if you are using the constraints with the useForm hook
// then you can bind the validator with the values so that the handler
// can be used with events
const handleBlur = useValidationWithValues(validator, values);
```
<a name="useValidation"></a>

## useValidation(validator, expectedFields) ⇒
A hook for performing validation.

**Kind**: global function
**Returns**: returns an useValidation object

| Param | Description |
| --- | --- |
| validator | A validation map or a validation function. |
| expectedFields | Define the fields required for validation. This is useful if you want certain fields to always be validated (ie required fields). If you are using a validation map, then this value will default to the keys of the validation map. |

**Example**
```js
// validate using validation maps
const {validateByName, errors} = useValidation({
name: (value) => value ? "" : "name is required!",
email: (value) => value ? "" : "email is required!"
});

// "email is required!"
await validateByName("email", "");
// {email: "email is required!"}
console.log(errors);

// validate with one validation function
const {errors, validate} = useValidation((values) => {
const errors = {name: "", email: ""};
if (!values.name) {
errors.name = "name is required!";
}
if (!values.email) {
errors.email = "email is required!";
}
return errors;
});

// {name: "", email: "email is required!"}
await validate({name: "John"});
// {name: "", email: "email is required!"}
console.log(errors);
```
2 changes: 1 addition & 1 deletion package.json
Expand Up @@ -33,7 +33,7 @@
"build": "yarn clean && webpack",
"build:prod": "yarn lint && yarn test && yarn bundle:prod",
"build:js": "yarn clean && yarn tsc ./src/index.ts --target ESNEXT --outdir dist &> /dev/null",
"build:docs:api": "yarn build:js || jsdoc2md ./dist/use*.js > ./docs/api.md"
"build:docs:api": "yarn build:js || jsdoc2md ./dist/use*.js > ./docs/API.md"
},
"peerDependencies": {
"react": "^16.8.0"
Expand Down
40 changes: 40 additions & 0 deletions src/useValidation.ts
Expand Up @@ -98,6 +98,46 @@ export function useValidation<T extends Validators>(
expectedFields?: string[],
): UseValidatorHookPartial<userSuppliedValue, T> | UseValidatorHook<userSuppliedValue>;

/**
* A hook for performing validation.
*
* @param validator A validation map or a validation function.
* @param expectedFields Define the fields required for validation.
* This is useful if you want certain fields to always be validated (ie required fields).
* If you are using a validation map,
* then this value will default to the keys of the validation map.
* @return returns an useValidation object
*
* @example
*
* // validate using validation maps
* const {validateByName, errors} = useValidation({
* name: (value) => value ? "" : "name is required!",
* email: (value) => value ? "" : "email is required!"
* });
*
* // "email is required!"
* await validateByName("email", "");
* // {email: "email is required!"}
* console.log(errors);
*
* // validate with one validation function
* const {errors, validate} = useValidation((values) => {
* const errors = {name: "", email: ""};
* if (!values.name) {
* errors.name = "name is required!";
* }
* if (!values.email) {
* errors.email = "email is required!";
* }
* return errors;
* });
*
* // {name: "", email: "email is required!"}
* await validate({name: "John"});
* // {name: "", email: "email is required!"}
* console.log(errors);
*/
export function useValidation(
validator: Validators | SingleValidator<userSuppliedValue>,
expectedFields?: string[],
Expand Down

0 comments on commit e6c00da

Please sign in to comment.