18
How To Efficiently Update React State For Multiple DOM Inputs Using the useReducer() Hook
This article assumes some basic familiarity with the useReducer()
hook. Examples are using react-bootstrap
but you don't need to be using it in your own project for this to work.
Assuming this state object...
const initState = {
firstName: "",
lastName: "",
street: "",
aptSuite: "",
city: "",
stateName: "",
zipcode: "",
date: "",
title: "",
status: "fillingOutForm",
};
Assuming a form input element structured like this...
<Form.Label htmlFor="user-first-name">First name</Form.Label>
<Form.Control
type="text"
name="FIRSTNAME" // Used for the action type
id="user-first-name"
value={formState.firstName} // formState from useReducer
required
onChange={(e) => {
const name = e.target.name;
const value = e.target.value;
dispatch({type: "CHANGE_" + name, payload: value });
}}
/>
You could have a separate action type within the reducer function for each DOM input such as...
switch (type) {
case CHANGE_FIRSTNAME:
// Return modified state.
case CHANGE_LASTNAME:
// Return modified state.
case CHANGE_STREET:
// Return modified state.
default:
return state;
}
This is inefficient however.
The solution to this inefficiency is to abstract outwards in the reducer function.
Given this onChange
handler...
// For example, the DOM input attribute name is 'firstName'
onChange={(e) => {
const field = e.target.name;
const value = e.target.value;
dispatch({
type: "CHANGE_INPUT",
payload: {
value,
field,
},
});
}}
...the reducer function could contain this...
function formReducer(state, action) {
const { type, payload } = action;
switch (type) {
case "CHANGE_INPUT":
return { ...state, [payload.field]: payload.value };
default:
return state;
}
}
Normally one would have more cases in the reducer function but this example is simplified for educational purposes
In the code above, a computed property name is used to take the attribute name of the element ('firstName') and update state in the right place. In this case...
const initState = {
firstName: "Whatever was type in by user",
// Rest of state properties...
}
Remember how to access the data needed using computed property names. You need to wrap the dot notation object accessor for the action payload object in brackets.
return { ...state, [payload.field]: payload.value };
Optimization of code length can be achieved by moving code from the onChange()
handler to its own function. It might even be more descriptive to change the name to something like updateStateWithInputValue
.
const changeDOMInput = (e) => {
const field = e.target.name;
const value = e.target.value;
dispatch({
type: "CHANGE_INPUT",
payload: {
value,
field,
},
});
};
onChange={(e) => {
changeDOMInput(e);
}}
I hope this helps!
18