DEV Community

Cover image for How To Efficiently Update React State For Multiple DOM Inputs Using the useReducer() Hook
Kyle Williams
Kyle Williams

Posted on

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.

Efficient VS Inefficient

HTML form with inputs for name and address
Any DOM structure of HTML inputs would do, but let's say for example you have an HTML form such as the one above. You want React to update state for every change of the input by the user.

Inefficient

Assuming this state object...

  const initState = {
    firstName: "",
    lastName: "",
    street: "",
    aptSuite: "",
    city: "",
    stateName: "",
    zipcode: "",
    date: "",
    title: "",
    status: "fillingOutForm",
  };
Enter fullscreen mode Exit fullscreen mode

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 });
    }}
/>
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

This is inefficient however.

Efficient

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,
    },
  });
}}
Enter fullscreen mode Exit fullscreen mode

...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;
    }
  }
Enter fullscreen mode Exit fullscreen mode

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...
}
Enter fullscreen mode Exit fullscreen mode

Gotchas

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 };

Further Cleaning

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);
}}
Enter fullscreen mode Exit fullscreen mode

I hope this helps!

Connect With Me!

www.kylewcode.com
Twitter
LinkedIn
GitHub

Top comments (3)

Collapse
 
ivanbozhkov profile image
Ivan Bozhkov

Which is the heavy operation we are trying to avoid and how does one solution handle it better than the other? 😵‍💫

Collapse
 
kylewcode profile image
Kyle Williams

I wanted to avoid having more actions than was necessary. The first solution was an action for each input and the other a single action to handle them all. The second solution is better because it make the code easier to read and follow. If there's a problem with the logic, I'd just have to look under a single action to debug.

Collapse
 
kylewcode profile image
Kyle Williams

By heavy do you mean time/space complexity? If so, I couldn't really say, but what I did was geared more towards readability.