- boilerplate-free field label and error rendering
- validation tests and error messages as data and functions with support for validations conditioned upon form data
- library of common form fields with support for supplying your own field components
- use default store (component state) or supply your own functions describing how to write field values (you're responsible for supplying a
values
prop) - model first architecture-ready; bundle validations and data transformations into a single prop
- observable
- toggle
validateAsYouGo
- support for complex form data structures including collections, nested objects and collections, etc
- support for collections with boilerplate management of persistent and temporary data
npm install @curiouser/react-forms
yarn add @curiouser/react-forms
A simple login form with validation that requires both fields to be filled in and a password of at least 6 characters.
import React from 'react';
import { Form, validator } from '@curiouser/react-forms';
import { PasswordField, TextField } from '@curiouser/react-forms';
import '@curiouser/react-forms/dist/index.css';
const formProps = {
formName: 'my-form',
initialValues: {
password: '',
username: '',
},
validations: [{
names: [ 'username', 'password' ],
tests: [[ validator.tests.required, validator.messages.required ]],
}, {
names: [ 'password' ],
tests: [[ validator.tests.minLength(6), validator.messages.minLength(6) ]],
}],
}
export default function MyForm () {
const form = React.useRef();
const handleSubmit = React.useCallback(() => {
if (!form.current.validate() || form.current.state.isLoading) return;
const formData = form.current.getData();
// do something with formData...
}, []);
return (
<Form ref={form} {...formProps}>
<form onSubmit={handleSubmit}>
<div className="form__fields">
<TextField label="Name" name="username" />
<PasswordField label="Password" name="password" />
</div>
<button type="submit">Sign in</button>
</form>
</Form>
);
}
import React from 'react';
import { Form, util, validator } from '@curiouser/react-forms';
import { PasswordField, TextField } from '@curiouser/react-forms';
import '@curiouser/react-forms/dist/index.css';
class MyForm extends Form {
static defaultProps = {
...Form.defaultProps,
formName: 'my-form',
initialValues: {
password: '',
username: '',
},
validations: [{
names: [ 'username', 'password' ],
tests: [[ validator.tests.required, validator.messages.required ]],
}, {
names: [ 'password' ],
tests: [[ validator.tests.minLength(6), validator.messages.minLength(6) ]],
}],
};
constructor (...args) {
super(...args);
util.bindMethods(this);
}
handleSubmit () {
if (!this.validate() || this.state.isLoading) return;
const formData = this.getData();
// do something with formData...
}
render () {
return super.render(
<form className="form" onSubmit={this.handleSubmit}>
<div className="form__fields">
<TextField label="Name" name="username" />
<PasswordField label="Password" name="password" />
</div>
<button type="submit">Sign in</button>
</form>
);
}
}
Run all of the form examples in your browser with yarn start
. Here are some common examples:
- Render form with renderProp and read form values for dynamic rendering
- Store form data elsewhere (redux perhaps)
- Render a more complex form with a nested data structure
- Observe your form (share the state)
- React to form and field updates with onChange prop
Form fields are broken into two, one representing the field (with label, error messaging and className generation) and the actual input/control component.
import React from 'react';
import Field from 'form/dist/components/fields/Field.jsx';
import NativeSelect from './NativeSelect.jsx';
export default function NativeSelectField (props) {
return <Field {...props} ref={props.forwardedRef} component={NativeSelect} type="select" />
}
import React from 'react';
import { util } from '@curiouser/react-forms';
export default function NativeSelect ({ forwardedRef, getValue, id, options, placeholder, required = true, setValue }) {
const handleChange = React.useCallback((e) => setValue(e.target.value), [ setValue ]);
return (
<select id={id} onChange={handleChange} ref={forwardedRef} value={getValue()}>
{util.renderIf(placeholder, () => (
<option disabled={required} value="">{placeholder}</option>
))}
{options.map(o => (
<option key={o.value} value={o.value}>{o.label}</option>
))}
</select>
);
}
You're responsible for importing or linking the stylesheet with import '@curiouser/react-forms/dist/index.css';
or any other way you like, it's just a css file. The package styles don't try to do anything pretty for you, just provide functional styles. Class names try to follow the BEM naming convention.
MIT © curiousercreative