A lightweight react form validation library that uses HOCs (higher order components) for functional programming, with typescript support.
yarn add react-form-validation-context
Or
npm install --save react-form-validation-context
import {
Form,
withForm,
validators,
withFormButton,
FormErrors // Optional, for custom errors
} from "react-form-validation-context";
withForm
is a higher order component that can wrap any kind of user input.
Below are some examples of how to create some form connected components
Javascript
const Input = withForm(({ value, onChange, error, showErrors }) => (
<div>
<input type="text" onChange={v => onChange(v)} value={value} />
{showErrors && error && <span>{error}</span>}
</div>
));
Typescript
export const InputComponent: React.SFC<any> = ({
value,
onChange,
error,
showErrors
}: any) => (
<div>
<input type="text" onChange={onChange} value={value} />
{showErrors && error && <span>{error}</span>}
</div>
);
const Input = withForm(InputComponent);
Javascript
const Dropdown = withForm(
({ value, onChange, error, showErrors, options}) => (
<div>
<select onChange={onChange}>
{options.map(({ label, val }) => (
<option key={val} checked={val === value} value={val}>
{label}
</option>
))}
</select>
{showErrors && error && <span>{error}</span>}
</div>
)
);
Typescript
export const DropdownComponent: React.SFC<any> = ({
value,
onChange,
error,
showErrors,
options
}: any) => (
<div>
<select onChange={onChange}>
{options.map(({ label, val }: { label: string; val: string }) => (
<option key={val} selected={val === value} value={val}>
{label}
</option>
))}
</select>
{showErrors && error && <span>{error}</span>}
</div>
);
const Dropdown = withForm(DropdownComponent);
Javascript
const Radios = withForm(
({ value, onChange, error, showErrors, options, id }) => (
<div>
{options.map(({ label, val }) => (
<React.Fragment key={val}>
<input
type="radio"
onChange={() => onChange(val)}
name={id}
checked={value === val}
/>
<label onClick={() => onChange(val)} htmlFor={id}>
<span>{label}</span>
</label>
</React.Fragment>
))}
{showErrors && error && <span>{error}</span>}
</div>
)
);
export const RadioComponent: React.SFC<any> = ({
value,
onChange,
error,
showErrors,
options,
id
}: any) => (
<div>
{options.map(({ label, val }: { label: string; val: string }) => {
const handleChange = () => onChange(val);
return (
<React.Fragment key={val}>
<input
type="radio"
onChange={handleChange}
name={id}
checked={value === val}
/>
<label onClick={handleChange} htmlFor={id}>
<span>{label}</span>
</label>
</React.Fragment>
);
})}
{showErrors && error && <span>{error}</span>}
</div>
);
const Radios = withForm(RadioComponent);
withFormButton
is a higher order component that can a submit or action button
Javascript
const Button = withFormButton(({ children, ...rest }) => (
<button type="button" {...rest}>
{children}
</button>
));
Typescript
const ButtonComponent: React.SFC<any> = ({ children, ...rest }) => (
<button type="button" {...rest}>
{children}
</button>
);
const Button = withFormButton(ButtonComponent);
Errors will be passed to the components by default.
To suppress inline errors you can add the prop hideErrors
.
For example:
<Input hideErrors ... />
You can use the FormErrors
component to display errors for specific components or for all errors on the form.
<FormErrors /> // will display all form errors
<FormErrors className="some-style" /> // to style errors you may pass props to parent container
<FormErrors errorsFor="email" /> // display errors for a single component
<FormErrors style={{color: 'red'}} errorsFor={["name", "email"]} /> // display errors for multiple components
// use a custom renderer
<FormErrors
render={(error, idOfInputWithError) => (
<span key={idOfInputWithError} className="error-styles">
{error}
</span>
)}
/>
validators
contain a set of validators for performing input validations
validators.requiredWithMessage
validators.required
validators.email
validators.maxLength
validators.minLength
validators.exactLength
validators.minValue
validators.maxValue
validators.maxFloatValue
validators.decimalWithDot
validators.number
validators.decimalWithCommaDot
validators.nationalInsurance
validators.alphaNumeric2WithMessage
validators.alphaNumeric2
validators.onlyAlphaNumeric
validators.address
validators.postcode
validators.allowedValues
class App extends Component {
state = {
name: "",
email: ""
};
submit() {
console.log(this.state);
}
render() {
return (
<div className="App">
<Form>
<FormErrors />
<Input
id="name"
value={this.state.name}
onChange={e => this.setState({ name: e.target.value })}
validations={[validators.requiredWithMessage('Please enter a name'), validators.maxLength(20)]}
/>
<Input
id="email"
value={this.state.email}
onChange={e => this.setState({ email: e.target.value })}
validations={[validators.required, validators.email]}
/>
<Dropdown
id="age"
value={this.state.age}
onChange={e => this.setState({ age: e.target.value })}
options={[
{ label: "one", val: 1 },
{ label: "ten", val: 10 },
{ label: "twenty", val: 20 },
{ label: "thirty", val: 30 }
]}
validations={[validators.required, validators.minValue(15)]}
/>
<Radios
id="status"
value={this.state.status}
onChange={status => this.setState({ status })}
options={[
{ label: "alive", val: "alive" },
{ label: "dead", val: "dead" },
{ label: "on holiday", val: "on holiday" }
]}
validations={[
validators.requiredWithMessage("What is your status?")
]}
/>
<Button onClick={() => this.submit()}>Submit...</Button>
</Form>
</div>
);
}
}
Dave Nicholas, Martin Carder, Ekta Wadhwani, Mariana Nicholas