UNPKG

@shopify/react-form-state

Version:
232 lines (189 loc) 6.6 kB
# Validators ## Built in validator functions The library makes a number of validation factory functions available out of the box that should help with common use cases. ```typescript import {validators} from '@shopify/react-form-state'; ``` ```typescript // input has a length > the given number lengthMoreThan(length: number, errorContent: ErrorContent): Validator // input has a length < the given number lengthLessThan(length: number, errorContent: ErrorContent): Validator // input is a numeric string numericString(errorContent: ErrorContent): Validator // input must be a non-empty string requiredString(errorContent: ErrorContent): Validator // input cannot be null/undefined required(errorContent: ErrorContent): Validator ``` We can pass these directly into the `validators` prop of `<FormState />`. ```typescript import {TextField} from '@shopify/polaris'; import FormState, {validators} from '@shopify/react-form-state'; function MyComponent() { return ( <FormState initialValues={{ title: 'Cool title', description: 'Cool product', quantity: 0, }} validators={{ title: validators.lengthLessThan(10, 'That title is too long') quantity: [ validators.required('Products must have a quantity'), validators.numericString('Quantity must be numeric'), ], }} > {formDetails => { const {fields} = formDetails; return ( <form> <TextField label="Title" {...fields.title} /> <TextField label="Description" {...fields.description} /> <TextField label="Quantity" {...fields.description} /> </form> ); }} </FormState> ); } ``` ## Building reusable validators As the number of custom validators in your app grows, you'll likely want to reuse them. This can be tricky because you'll usually want to support custom error messages depending on context. `@shopify/react-form-state` ships with a small DSL for building your own validators. ```typescript import {validate} from '@shopify/react-form-state'; ``` the `validate` function takes a `Matcher` function and some error content, and returns a `Validator` that works with `FormState`'s `validators` prop. ```typescript export function validate<Input>( matcher: Matcher<Input>, errorContent: ErrorContent, ): (input: Input) => ErrorContent | void; ``` You can use `validate` to generate handlers cleanly from your own functions. ```typescript //utilities.ts export function isValidEmail(input: string) { // example only return /+\@.+\..+/.test(string); } ``` ```typescript //MyPage.ts import FormState, {validate} from '@shopify/react-form-state'; import {isValidEmail} from './utilities'; function MyComponent() { return ( <FormState initialValues={email: ''} validators={{ email: validate(isValidEmail, 'invalid email') }} > {/* ...some ui */} </FormState> ); } ``` Or take it a step further by making a generic shared validator. ```typescript //custom-validators.ts import {validate, ErrorContent} from '@shopify/react-form-state'; export function validEmail(input: string, errorContent: ErrorContent) { // example only return validate(input => /+\@.+\..+/.test(input), errorContent); } ``` ```typescript // MyPage.ts import FormState from '@shopify/react-form-state'; import {validEmail} from './custom-validators'; function MyComponent() { return ( <FormState initialValues={email: ''} validators={{ email: validEmail('Email is invalid'), }} > {/* ...some ui */} </FormState> ); } ``` `validate`-generated functions also support taking an error-generating functions as their last parameter. Suppose we want to include the current field value in our error message; we could update the above example to use this technique. ```typescript // MyPage.ts import {validEmail} from './custom-validators'; function MyComponent() { return ( <FormState initialValues={email: ''} validators={{ email: validEmail((input) => `${input} is not a valid email`), }} > {/* ...some ui */} </FormState> ); } ``` ### Validating empty inputs By default, functions generated by `validate` will automatically pass if the given input is empty. Sometimes, most often when building a custom `required` style check, you will want to run your validator _even on empty input_. This can be done by using `validateRequired` instead of `validate`. ```typescript //MyPage.ts import FormState, {validateRequired} from '@shopify/react-form-state'; import {isValidEmail} from './utilities'; function MyComponent() { return ( <FormState initialValues={email: ''} validators={{ email: validateRequired(isValidEmail, 'invalid email') }} > {/* ...some ui */} </FormState> ); } ``` ## Compound validators All of the validation we've done so far has been concerned with scalar value fields. Its possible to write validators for arrays and objects using the syntax described above, but it would be difficult to handle passing errors down intelligently. Its great for things like checking that there are a set number of items in the array, but terrible for checking individual subfield values. ```typescript validators={{ variants(input) { if (input.length > 10) { return 'too many variants!'; } } }} ``` Because of these difficulties, this package also contains some helpers for writing validators for complex fields that work with `<FormState.Nested />` and `<FormState.List />` to let us propagate errors down the same way as we do for regular fields. ### `validateNested()` ```typescript import {validateNested} from '@shopify/react-form-state'; ``` The validation helper companion for `<FormState.Nested />` lets you define field level validations for nested fields using the same API as you would use for flat scalar fields. ```typescript validators={{ firstVariant: validateNested({ price: validators.numericString('variant price should be a number'), sku: validators.lengthLessthan(3, 'variant SKU must be shorter than 3 characters'); }); }} ``` ### `validateList()` ```typescript import {validateList} from '@shopify/react-form-state'; ``` ```typescript validators={{ variants: validateList({ price: validators.numericString('variant price should be a number'), sku: validators.lengthLessthan(3, 'variant SKU must be shorter than 3 characters'); }); }} ```