react-form-state-reducer
v0.1.1
Published
A reducer for easy handling React form state.
Readme
Purpose
Provide a generic reducer to handle the state of any form.
Usage
TS:
import * as FormStateProvider from "react-form-state-reducer";
function ExampleForm() {
// Provide initial values to populate the form inputs.
const record: FormStateProvider.UserData = {
orderid: 1,
orderDate: '2024-02-01T19:40',
name: 'Foo Bar',
size: 'medium',
toppings: ['chocolate'],
comments: 'Coffee with sugar, no milk',
drop: 'delivery',
acceptance: 'T',
color: "#00ff00"
};
// Initialize form state and get dispatch function
const [state, dispatch] = FormStateProvider.useReducer(record);
// Optionally set a handler to reset the form state to the initial values
function resetHandler(e: any) {
e.preventDefault();
// Call dispatch with the 'reset' action to reset the form
dispatch({
type: 'reset',
data: record
});
}
// Set a generic change handler to keep the form state updated with the input values
function changeHandler(e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>) {
// Call dispatch with the 'keepState' action to update the form state with the input value
dispatch({
type: 'keepState',
target: e.target
});
}
// In this example we set an onReset handler to reset the form state to the initial values
// Set the "name" and "value" attributes on each input to match the corresponding record attribute.
// An onChange handler is set to keep the form state updated with the input values.
return(<form name="my-form" onReset={resetHandler} onSubmit={(e) => { e.preventDefault(); }}>
{/* Hidden input example, since this is readonly we do not need an onChange handler here */}
<input type="hidden" name="orderid" value={state.orderid} />
{/* Text input example */}
<fieldset>
<legend>Text input example</legend>
<input type="text" name="name" value={state.name} onChange={changeHandler} />
</fieldset>
{/* datetime-local input example */}
<fieldset>
<legend>datetime-local input example</legend>
<input type="datetime-local" name="orderDate" value={state.orderDate} onChange={changeHandler} />
</fieldset>
{/* Select input example. Optionally add a "multiple" attribute to enable multiple selections. */}
<fieldset>
<legend>Select input example</legend>
<select name="size" value={state.size} onChange={changeHandler}>
<option value=""></option>
<option value="small">Small</option>
<option value="medium">Medium</option>
<option value="large">Large</option>
<option value="extra">Extra Large</option>
</select>
</fieldset>
{/* Textarea input example */}
<fieldset>
<legend>Textarea example</legend>
<textarea name="comments" value={state.comments} onChange={changeHandler} />
</fieldset>
{/* Radio input example
All inputs shares a common name and each input has its own value.
A handy function getChecked is provided to properly set the "checked" attribute based on the input state */}
<fieldset>
<legend>Radio input example</legend>
<label>
<input type="radio" name="drop" value="delivery" checked={FormStateProvider.getChecked(state.drop, 'delivery')} onChange={changeHandler} />
Delivery
</label>
<label>
<input type="radio" name="drop" value="takeaway" checked={FormStateProvider.getChecked(state.drop, 'takeaway')} onChange={changeHandler} />
Take away
</label>
</fieldset>
{/* Single checkbox example */}
<fieldset>
<legend>Single checkbox example</legend>
<input type="checkbox" name="acceptance" value="T" checked={FormStateProvider.getChecked(state.acceptance, 'T')} onChange={changeHandler} />
Opt-in
</fieldset>
{/* Multiple checkbox example
All inputs shares a common name and each input has its own value.
Add a "multiple" attribute to enable multiple selections
A handy function getChecked is provided to properly set the "checked" attribute based on the input state */}
<fieldset>
<legend>Checkbox multiple example:</legend>
<label>
<input type="checkbox" name="toppings" value="chocolate" multiple checked={FormStateProvider.getChecked(state.toppings, 'chocolate')} onChange={changeHandler} />
Chocolate
</label>
<label>
<input type="checkbox" name="toppings" value="cream" multiple checked={FormStateProvider.getChecked(state.toppings, 'cream')} onChange={changeHandler} />
Cream
</label>
</fieldset>
{/* Color input example */}
<fieldset>
<legend>Color input example:</legend>
<input type="color" name="color" value={state.color} onChange={changeHandler} />
</fieldset>
{/* File input example. Optionally add a "multiple" attribute to enable multiple selections. */}
<fieldset>
<legend>File input example:</legend>
<input type="file" name="bill" value={state.bill} onChange={changeHandler} />
</fieldset>
<button type="reset">Reset</button>
<button type="submit">Submit</button>
</form>);
} In order to work properly, a name attribute is required for each managed input. This is because event.target.name is used as the key under which the state is stored.
Initial state values are converted to string for single values and string[] for multiple values when calling FormStateProvider.useReducer with initial data. This is because event.target.value also returns either string or string[]
Support for inputs with multiple values:
Some inputs are meant to be multiple, such as:
- Selects with the
multipleattribute. - Checkboxes with the same
nameattribute. (*) - Inputs with
type="file"andmultipleattributes.
For such cases, a string[] type is used to store the input values.
(*) Notes on Checkbox multiple:
To properly recognize checkboxes as multiple, add a multiple attribute on each option.
Change the state at will
Call dispatch with the setValue action to update the input state:
dispatch({
type: 'setValue',
name: 'name',
value: 'Bar Foo'
});