Forms with React and Redux made simple: start with the bare minimum, no magic, then use composition (components, functions, global and local states) to create more complex forms. This package has no external dependencies and will have a very little impact on your bundle (< 1.5kB gzipped).
This package is similar to react-reformed, and I encourage you to look at it and read its README.
This package contains:
- A reducer factory
- A higher-order component wrapping
connect
(from react-redux package) - Validation helpers
This package does not contain:
- Components: you can easily create your own form components, for native and web
- Event handlers and UI elements state management (focused, blurred, touched, submitted, etc...): you can choose to include them in your redux store, or simply use local state
- Async validation of input values: use state and component composition to solve those more complex problems
- Currently doesn't work with nested objects
1. Get started in no time
2. API reference
3. Build your own API
1. Include your forms in your store
import { createStore, combineReducers } from 'redux';
import { createFormReducer } from 'react-redux-reformed';
const reducer = combineReducers({
form: createFormReducer('myForm', { name: '' })
})
2. Bind your form to your store
import React, { PureComponent } from 'react';
import { connectForm } from 'react-redux-reformed';
import { isRequired } from 'react-redux-reformed/validate';
class MyForm extends PureComponent {
submitHandler = (evt) => {
evt.preventDefault();
// Send the model somewhere
};
nameChangeHandler = (evt) => setFormField('name', evt.target.value);
render() {
const { model, modelValidity, setFormField, resetForm } = this.props;
return (
<form onSubmit={ this.submitHandler }>
<input name='name' value={ model.name } onChange={ this.nameChangeHandler } />
<button type='submit' disabled={ !modelValidity.valid }>Submit</button>
<button type='button' onClick={ resetForm }>Reset</button>
</form>
);
}
}
const formName = 'myForm';
const formSelector = (state) => state.form;
const validators = [
isRequired('name')
]
export default connectForm(formName, formSelector, validators)(MyForm);
And that's it, you have a basic form set up, connected to your redux store and with validation.
A form reducer can react to 3 types of actions:
- Reset state actions: to reset the state
- Set state actions: to amend the current state
- Replace state actions: to replace the current state
Set and replace work similarly to local state in React (setState
, replaceState
).
import { createFormReducer } from 'react-redux-reformed';
const reducer = createFormReducer(
'myForm',
{ name: '' },
{
resetActions: [
(action) => action.type === 'CREATE_USER_RECEIVE' && !action.error
]
}
);
formName
: the form name, so the reducer knows if actions can be applied or ignoredinitialFormState
: to initialise your formoptions
: see below
Options:
resetActions
: a list of actions which can reset your form reducer. The list can contain action types (strings) or functions of actions (returning a boolean). The last one is useful for piggy backing on other actions like successful AJAX requests (see example above).mergeState
: a function to merge state received inSET_FORM_STATE
. By default, it performs a shallow merge.
import { connectForm } from 'react-redux-reformed';
import { isRequired } from 'react-redux-reformed/validate';
const MyConnectedForm = connectForm(
'myForm',
(state) => state.form
)(MyForm);
formName
: the form name, so the actions can be created with it (and touch the right reducer)formSelector
: so your form can be located (connectForm has no idea where your form is otherwise)validators
: a list of validation functions, seevalidator
function below
The following props will be available:
model
: the form object stored in your redux storemodelValidity
: if validators are supplied (optional), a validity report is added to the props- Action creators:
setFormState
,replaceFormState
,setFieldState
,resetFormState
A model validity looks like the following (don't call one of your fields valid
):
const modelValidity = {
valid: false,
name: {
valid: true,
required: true
},
email: {
valid: false,
require: true,
email: false
}
};
import { validator } from 'react-redux-reformed';
const minLength = (fieldName, min) =>
validator(
fieldName,
(val) => val && val.length >= min,
'minLength'
);
fieldName
: the name of the field being validatedvalidationFn: a function of the field value, returning
true(if valid) or
false` (if not valid)name
: the validator name. Optional ifvalidationFn
is named
For reference purposes, a isRequired
validator is included (but that's the only one). Don't call one of your validators valid
.
You might want to compose selectors and actions together in your own connect
HOC, have validation in a selector or in a separate higher-order component, or simply compose things together differently.
import { createModelValidator, isRequired } from 'react-redux-reformed/validate';
const validateModel = createModelValidator(isRequired('name'));
validateModel({ name: 'hello' })
// Prints:
{
valid: true,
name: {
valid: true,
required: true
}
}
import { SET_FORM_STATE, setFormState } from 'react-redux-reformed/actions';
Action creator for action type SET_FORM_STATE
.
import { SET_FORM_STATE, setFormState } from 'react-redux-reformed/actions';
Action creator using setFormState
action creator.
import { REPLACE_FORM_STATE, replaceFormState } from 'react-redux-reformed/actions';
Action creator for action type REPLACE_FORM_STATE
.
import { RESET_FORM_STATE, resetFormState } from 'react-redux-reformed/actions';
Action creator for action type RESET_FORM_STATE
.