Async friendly validation middleware for Redux
npm install redux-tsa   
Async friendly validation middleware for Redux
1. Gist
2. API
3. Performance
4. Complementary Libraries
5. Examples
``typescript${fieldKey} must be even
const even: SyncValidator
check({ field }) {
return Number.isInteger(field / 2)
},
error({ fieldKey }) {
return ;
},
};
const goodPerson: AsyncValidator
check({ field }) {
// backgroundCheck calls an API that performs a background check
return backgroundCheck(field);
},
error({ field }) {
return ${field} is a criminal;`
},
};`$3
typescript
type TransactionType = "DEPOSIT" | "WITHDRAWAL";
interface Transaction extends Redux.Action {
type: TransactionType;
target: string;
amount: number;
}
function withdrawal(target: string, amount: number): Transaction {
const action: Transaction = {
amount,
target,
type: "WITHDRAWAL",
};
// you can mix sync and async validators, they will be normalized internally
const validatorMap: ValidatorMap
target: [goodPerson],
amount: [fundsAvailable, even],
};
return validate({ action, validatorMap });
}
`
`typescript`
// inside a reducer
case "WITHDRAWAL":
if (isTSAErrorAction(action)) {
return { ...state, errors: action.fieldErrors! };
} else {
return initialState;
}
* reduxTSA
* validate
* validateSync
* isTSAErrorAction
`reduxTSA` is the middleware:
`typescript
import reduxTSA from "redux-tsa";
applyMiddleware(reduxTSA)
`
`validate` specifies async validation for an action:
`typescript
/**
* mode specifies the max number of errors that should be captured per field
* fieldErrors and processErrors both affect the error count
* mode defaults to Infinity, which captures as many errors as possible
* mode=0 specifies binary validation
* a lower mode means faster validation because the validators for a given field are raced
*/
interface ValidateInput {
action: A;
validatorMap: ValidatorMap;
mode?: number;
}
validate(input: ValidateInput) => A;
`
`validateSync` specifies sync validation for an action:
`typescript
/**
* mode specifies the max number of errors that should be populated per field
* fieldErrors and processErrors both affect the error count
* mode defaults to Infinity, which captures as many errors as possible
* mode=0 specifies binary validation
* a lower mode means faster validation becuase sync validation is performed lazily
*/
interface ValidateSyncInput {
action: A;
validatorMap: types.SyncValidatorMap;
mode?: number;
}
validateSync(input: ValidateSyncInput) => A;
`
`isTSAErrorAction` is a type guard that is used to determine whether an action passed validation:
`typescript`
isError(action: TSAAction): action is TSAErrorAction
* AsyncValidator
* SyncValidator
* SyncValidatorMap
* Validator
* ValidatorMap
* TSAAction
* TSAError
* ErrorMap
`Only the above types/interfaces are exported. Other types/interfaces are also listed below for clarity.`
`typescript
// ValidatorInput is used for both AsyncValidators and SyncValidators
interface ValidatorInput {
fieldKey: K;
field: A[K];
action: A;
state: S;
}
// ProduceError is used for both AsyncValidators and SyncValidators
type ProduceError = (
input: ValidatorInput
) => TSAError;
type AsyncCheck = (
input: ValidatorInput
) => Promise
interface AsyncValidator {
check: AsyncCheck;
error: ProduceError;
}
`
`typescript
type SyncCheck = (
input: ValidatorInput
) => boolean;
interface SyncValidator {
check: SyncCheck;
error: ProduceError;
}
`
`typescript`
type SyncValidatorMap = {
[K in keyof A]?: Array
};
`typescript
type Validator =
SyncValidator | AsyncValidator;
`
`typescript
type Validator = SyncValidator | AsyncValidator;
type ValidatorMap = {
[K in keyof A]?: Array
};
`
`typescript
/**
* fieldErrors are the errors produced by your validators
* processErrors are the errors that occur when trying to run your validators (e.g. failed network request)
* fieldErrors and processErrors will be null only if mode=0
*/
interface ErrorActionHelp {
type: A[T];
error: boolean;
fieldErrors: types.ErrorMap | null;
processErrors: types.ErrorMap | null;
}
type ErrorAction = ErrorActionHelp;
type TSAAction = A | ErrorAction;
`
`typescript`
type TSAError = Error | string;
`typescript`
type ErrorMap = { [K in keyof A]?: TSAError[] };Performance
Redux TSA makes validation fast using the concept of a `mode`. `mode` is specified when calling either `validate` or `validateSync`, and specifies how many errors to capture per field. The lower the `mode`, the faster validation will be.
When performing async validation:
Redux TSA races* the validators for each field. Redux TSA is done validating a field as soon as `mode` number of errors are found.
Redux TSA runs the validators for each field concurrently*.
When performing sync validation:
Redux TSA runs the validators lazily*. If `mode`` number of errors were already found for a field, then Redux TSA will not run any more validators for that field.
1. Redux Transform: lets you transform the properties of an action in much the same way that Redux TSA lets validate the properties of an action.
An example application using Redux TSA:
- TypeScript
- JavaScript