README
redux-easy-forms
See it in action: REF DEMO
Full rewrite as of ver 0.3.0 -- smaller, cleaner, nicer API :)
Yet another forms solution
- Simplify dealing with client-side forms
- Define your form fields via a simple "schema" object
- Supply desired validation functions and error messages per field
- Use a friendly API to interact with your forms and data
- Obtain all JSX props for each input via a single API method call
Approach
REF exposes a small number of handy methods to cut down on common form-related chores. It maintains its "private" Redux state, eliminating the need for dispatching actions or connecting to the forms reducer directly. It is a fairly narrowly-focused abstraction, compatible with native HTML inputs and any React components which follow the same props (adaptable).
API Methods: clear | clearLiveErrors | get | getErrors | getLiveErrors | init | isDirty | isValid | props | restorePristine | saveAsPristine | set | setFocus
Install
npm install redux-easy-forms --save
or yarn add redux-easy-forms
- Define forms "schema" object and a validators object for ALL forms used throughout your app:
const schema = {
login: { // specify unique key per each form
password: { type: 'password' }, // unique name per input within each form
username: { type: 'text', placeholder: 'Username' } // specify type and any other valid props
},
agreement: {
pin: { type: 'tel', validators: 'isPin' }, // reference desired validator by key
agree: [{ // checkboxes/radios/select must be arrays
type: 'checkbox',
value: 'on',
validators: 'isAgree'
}],
duration: [
{ type: 'select-one', value: 'day', selected: true }, // can have init value, checked, or selected
{ type: 'select-one', value: 'week' },
{ type: 'select-one', value: 'month' },
],
}
};
const validators = {
isAgree: { fn: val => !!val.length, error: 'You must agree to proceed' },
isPin: [
{ fn: val => !!val, error: 'PIN is required' },
{ fn: val => val.length === 4, error: '4-digit PIN is required' }
],
}
- Add
reformsReducer
to your Redux store and wrap your app with theReformsProvider
:
import { reformsReducer, ReformsProvider } from 'redux-easy-forms';
const rootReducer = combineReducers({
reformsData: reformsReducer // <-- be sure to name it reformsData
/* ... */ // <-- your other reducers...
const store = createStore(rootReducer);
<Provider store={store}>
<ReformsProvider schema={schema} validators={validators}> // <-- beneath the Redux provider is great
<App />
</ReformsProvider>
</Provider>
- Then, for any component needing access to REForms, enhance it with the
withReforms
HOC:
import { withReforms } from 'redux-easy-forms'; // <--
class LoginPage extends React.Component { /* ... */ }
export default withReforms(LoginPage); // <--
Of course, you can also compose in connect
to any of your other reducers:
export default connect(
mapStateToProps,
mapDispatchToProps
)(withReforms(LoginPage)); // <--
Or, if you enjoy using the decorator syntax:
@connect(mapStateToProps, mapDispatchToProps)
@withReforms
class LoginPage extends React.Component { /* ... */ }
Usage
The enhanced component will now receive this.props.reforms
, which provides a handy API to your forms data:
const { reforms } = this.props;
To render an input field in JSX:
<input {...reforms.props('login', 'username')}/>
The props
method returns all necessary props in one swoop (including name
, value
, onChange
, etc.)
Other API examples:
reforms.setFocus('login', 'username'); // --> username input gets focused
reforms.set('login', {username: {value: 'Jane'}}); // --> 'Jane' appears in username field
reforms.get('login', 'username'); // --> 'Jane'
reforms.get('login'); // --> { username: 'Jane', password: '' }
reforms.isValid('login'); // --> true
API Reference
clear(form:String, [name:String])
Clear all fields within a given form, or a single field. Also reset the "touched" status, thus clearing any "live" validation errors.
reforms.clear('login')
clearLiveErrors(form:String, [name:String])
Reset the "touched" status of specified form field(s), thus clearing any "live" validation errors.
reforms.clearLiveErrors('login')
Object
or String|Array
get(form:String, [name:String]) ⇒ Get current form values (object of key-vals), or the value of a given form field (string, or an array of strings if multiple values). For multi-input arrays (checkboxes, radios, select-one) holding only one value element, it is delivered as a string.
reforms.get('login')
Object
or Array
getErrors(form:String, [name:String]) ⇒ Get validation errors for the entire form, or a given form field. Errors are arrays of strings, or an empty array (if no errors).
reforms.getErrors('login')
Object
or String
getLiveErrors(form:String, [name:String]) ⇒ Suitable for per-field errors while editing a form. Delivers only the first error, as long as the form/field is "touched" and not in focus.
reforms.getLiveErrors('login', 'username')
init(schema:Object)
Dynamically initialize additional form schema into REF. Existing form names will be "extended" in. Any validators referenced in the new schema must already be defined.
reforms.init({ 'note_2': { note: { type: 'text' } } })
Boolean
isDirty(form:String, [name:String]) ⇒ Check whether a form or a given field have changed from their "pristine" state. A form/field is considered "pristine" when first initialized, upon performing a set
, or saveAsPristine
.
reforms.isDirty('login')
Boolean
isValid(form:String, [name:String]) ⇒ Check whether a form or a given field have no validation errors.
reforms.isValid('login')
Object
props(form:String, name:String, [value:String]) ⇒ Get all JSX props for a given input field. For checkboxes and radios, the input's value is required (third argument). Returns the schema props along with REF-specific props, e.g. onChange
or onClick
handlers, ref
(used for setFocus
), plus onFocus
& onBlur
(to support getLiveErrors
logic).
reforms.props('login', 'username')
or reforms.props('login', 'agree', 'on')
restorePristine(form:String, [name:String])
Reset all form fields, or a given field, to their "pristine" state.
reforms.restorePristine('login')
saveAsPristine(form:String, [name:String])
Store current form state, or the state of a given field, as the new "pristine" state.
reforms.saveAsPristine('login')
set(form:String, payload:Object)
Set value(s) into one or more fields of a given form. The payload
must match the schema structure exactly, as it will be "extended" directly over the current field data. To be bullet-proofed in future versions, in the meantime, tread lightly!
reforms.set('login', { username: { value: 'me@example.com', disabled: true } })
setFocus(form:String, name:String)
Set focus to a given field.
reforms.setFocus('login', 'username')
Changelog
See Releases