:lollipop: React-Form-Reducer
A utility of React hooks and a context that provide a simpler form field management, in addition to support of frontend validation and backend validation error processing. The library uses the React built-in reducers and context with no dependencies to achieve a typescript oriented way of managing form state efficiently and heling developers with autosuggestion of form fields in both setters and getters.
> This package works best with TypeScript
![ts](https://badgen.net/badge/Built%20With/TypeScript/blue)
→ :feather: Lightweight with no dependencies
Install
npm install react-form-reducer
Simple Usage
A simple way of using this package is as follows:
import { useForm } from "react-form-reducer";
type FormInterface = {
name: string;
email: string;
}
const MyComponent = ()=>{
const { fields, assignFieldInput } = useForm<FormInterface>({
name: '',
email: ''
})
return (
<form>
<div>
<label>Name:</label>
<input
type="text"
{...assignFieldInput('name')}
/>
</div>
<div>
<label>Email:</label>
<input
type="email"
{...assignFieldInput('email')}
/>
</div>
</form>
)
}
export default MyComponent;
Examples
1. Simple Form:
Example using useHook
![Edit react-form-reducer--simple-demo](https://codesandbox.io/static/img/play-codesandbox.svg)
2. Stepped Form:
Example using FormContextProvider
and useFormContext
![Edit react-form-reducer--stepped-demo](https://codesandbox.io/static/img/play-codesandbox.svg)
3. Simple Validation Form
![Edit react-form-reducer--validation-demo](https://codesandbox.io/static/img/play-codesandbox.svg)
4. Partial validation in a context form (stepped form)
![Edit react-form-reducer--stepped-validation-demo](https://codesandbox.io/static/img/play-codesandbox.svg)
Documentation
This library provides both a hook and a context provider, the useForm
hook can be used for single component forms, and useFormContext
can be used for multi-component forms.
useForm
hook
This hook can be used for single component forms, and has plenty of usefull states and actions:
const {
fields,
setField,
setFields,
handleInputChange,
assignFieldInput,
assignFieldUI,
reset,
isDirty,
setIsDirty,
isBusy,
setIsBusy,
step,
setStep,
meta,
setMeta,
setAllMeta,
validate,
validationWatcher,
errors
} = useForm<
MyFormInterface,
MyMetaInterface
>({
...default values go here
},
{
onUpdateFields: (data)=> {}
validation: new Resolver
},
{
metafield: 'default',
...
});
Properties of useForm
& useFormContext
-
fields
The state object of form body ()
-
setField('name', value)
This can be used to sets a field value by name
-
setFields(data, isDirty?: boolean = true)
This can be used to sets all the fields values at once
If you wish set a few values only instead of all field values do the following:
setFields({...fields, name: 'Jane', surname: 'Doe'})
-
handleInputChange('name')
This can be used to directly handle ChangeEvents on HTML form elements, and automaticly assign the new values to the fields.
-
assignFieldInput('name')
This can be used to directly assign all usefull attributes to HTML form elements (input,textarea,select), this function will return an object with values for element attribute: value
, onChange
and disabled
, so it can be simply used this way on an input:
<input
type="text"
{...assignFieldInput("email")}
/>
-
assignFieldUI('name')
This is similar to assignFieldInput
but returns additional properties error
& helperText
, this is usefull for UI components that support those properties like MUI and :
<TextField
{... assignFieldUI("email")}
/>
-
reset()
This will reset all the field values to the default values
-
isDirty
& setIsDirty
This state can be used to detect if the user made changes to the field values and handle it.
-
isBusy
& setIsBusy
This state can be used to handle the loading and disabled logic, this state has to be set and reset as part of your form submission logic.
-
step
& setStep
This state that can be usefyll when handling stepped forms.
-
meta
& setMeta
& setAllMeta
This state is similar to the fields state, but isn't part of the validation of reset logic, can be usefull for when storing extra data that is used accross components but not passed with the form submission.
-
validate(fieldsToCheck?: string[])
This is the trigger for running hte validation, not passing any fields will trigger a full validation on all fields, otherwise if you pass an array of field names it will do a partial validation for them. More info on how to setup validation rules this can be found below in the validation section.
-
validationWatcher
This is a state validation response, it can be useful if you wish to display or print validation errors in real time.
-
errors
This is property that returns the useFormErrors
hook used by the form hook, the form hook will use this to process frontend validation errors, but you use it outside to process BackEnd errors outside the hook, more info on how to use this in the useFormErrors section below.
Options for useForm
& FormContextProvider
You can pass these options to the hook and context provider:
-
onUpdateFields: (data)=>data
This option is an event option that allows you to manipulate the fields state data everytime they are updated.
-
validation: new Resolver(...)
This option allows you to pass a validation resolver to provide the ability of validating fields, more info about this can be found in the validation section.
Context Provider : useFormContext
& FormContextProvider
The context provider and hook are helpful for when sharing the form state between multiple components (for example: A multi-step form).
-
Setting up provider context
const WrapperComponent = ()=>{
const FormProvider = FormContextProvider<MyFormInterface>;
return (
<FormProvider
defaultValues={{
email: '',
name: '',
type: '',
terms: false
}}
options={{
validation: new Resolver({
email: 'required|email',
name: 'required',
type: 'required',
terms: 'accepted'
})
}}
>
<MyFormComponent />
</FormProvider>
);
}
-
Usage of useFormContext
This hook will return the same properties as useHoo
k, just make sure you call this hook inside the context provider you setup before:
const MyFormComponent = ()=>{
const { fields } = useFormContext<MyFormInterface>();
return (....);
}
useFormErrors
a hook that handles errors
This hook is provided as the error
property in the previous hooks, but can also be used on its own.
const errors = useFormErrors();
errors.set(data);
errors.add('field_name', 'Invalid value');
error.has('field_name');
error.hasAny(['field_1', 'field_2']);
error.get('field_name');
error.first('field_name');
error.forget('field_name');
error.forget();
The error messages should follow the Errors
type exported by the package, an example of a valid error data:
{
'name: [
'This field is required'
],
'password': [
'Password should be at least 6 characters',
'No whitespace allowed'
]
}
Validation
To use validation on form fields, you need to install the resolver library react-form-reducer-validator
:
npm i react-form-reducer-validator
The resolver wraps and uses the validatorjs
library, the library allows for a neat & simple way of adding validation rules. and the resolver allows for partial validation which is useful for stepped forms. You can also build and use your own resolver library if you like!
import { Resolver } from 'react-form-reducer-validator';
type MyValidatedFormType = {
name: string;
email: string;
password: string;
password_confirmation: string;
terms: boolean;
};
export function ValidatedForm() {
const {
fields,
handleInputChange,
validate,
errors
} = useForm<MyValidatedFormType>(
{
name: '',
email: '',
password: '',
password_confirmation: '',
terms: false
},
{
validation: new Resolver(
{
name: 'required',
email: 'required|email',
password: 'required',
password_confirmation: 'required|same:password',
terms: 'accepted'
},
{
'accepted.terms': 'Please accept the :attribute',
'same.password_confirmation': "Passwords don't match"
}
)
}
);
const handleSubmit = async ()=>{
const { passed } = await validate()
if(!passed){
}
}
}
Further documentation on the validation resolver can be found on its repo: https://github.com/sweetscript/react-form-reducer-validator
Handle Backend Validation Errors
To process the errors returned from the backend you can use the useFormErrors
hook, as long as they're in the suitable format.
Contribution
![GitHub issues](https://img.shields.io/github/issues/sweetscript/react-form-reducer)
Feedback, Issue reports, suggestions and contributions are appreciated and welcome.
https://github.com/sweetscript/react-form-reducer/issues
https://github.com/sweetscript/react-form-reducer/discussions
majid@sweetscript.com