Victor Morales
Victor Morales

Reputation: 43

How to handle a form with many fields in React

I am a newbie. Im building a web application with React. I have a form with 18 fields.

I currently have a useState with initial value

 const clientObj = { //18 fields };
 const [client, setClient] = useState(clientObj);

For each input, I have something like this (example with field name):

<Form.Group>
    <Form.Control 
      onChange={ e => { setClient( prev => ({...prev, name:e.target.value}))}}
      placeholder="Your name"
      type="text"
      required/>                                                
</Form.Group>

It works, however I doubt it is efficient. I want my web application to work efficiently. How should I handle a form with so many fields? Thanks :)

Upvotes: 2

Views: 6917

Answers (5)

Bruno Monteiro
Bruno Monteiro

Reputation: 4519

TL;DR;

You're good, leave your code like that. Or use a useReducer hook if you don't want to have multiple useState (which is not a problem). Or follow the recommendation from Drew Reese answer to have a single object.

More info

It works, however I doubt it is efficient. I want my web application to work efficiently.

What do you mean by that? If you're talking about performance (speed, memory use, etc.), you don't need to worry about it. React already does a good job to make your app efficient. Browser are more than capable to handle a form with many fields. It's a mistake to try to optmize things if you don't have a really good reason for it. Here is a nice article (although it talks about useMemo and useCallback) about this idea.

If you're talking about too many rerenders, you're probably fine as well! Don't get me wrong, it's better to have a cleaner code and avoid unnecessary rerenders. But you need to think about the balance of having a "complex code that makes your app more efficient in theory, but no real impact when using it" VS "a simple code, easy to understand/extend that is less efficient in theory, but when running the app you don't really feel that impact".

All that said, if you start to have infinite loops or states messing with each other, you definitely need to fix that. But then this is a bug and not an efficiency problem 😁

Here's another article that may help you understand more about rerenders.

Upvotes: 4

Drew Reese
Drew Reese

Reputation: 203418

Typically when working with form inputs and nested state you assign a name attribute to each input and use a single onChange handler. Since you are also using local component state and an onChange handler then you might want to also ensure your inputs are fully controlled by also specifying the value prop on the inputs.

const clientObj = { //18 fields };
const [client, setClient] = useState(clientObj);

const onChange = event => {
  const { name, value } = event.target;
  setClient(prevState => ({
    ...prevState,  // shallow copy all previous state
    [name]: value, // update specific key/value
  }));
};

...

<Form.Control
  name="name"
  value={client.name}
  onChange={onChange}
  placeholder="Your name"
  type="text"
  required
/>
<Form.Control
  name="lastName"
  value={client.lastName}
  onChange={onChange}
  placeholder="Your last name"
  type="text"
  required
/>
<Form.Control
  name="age"
  value={client.age}
  onChange={onChange}
  placeholder="Your age"
  type="text"
  required
/>
...etc...

Upvotes: 8

Chris Case
Chris Case

Reputation: 31

I would suggest that as you tinker with your forms, you consider implementing a component for each type of form field you want to handle, so you can customize it and reuse it. If you change your mind as you go, you may find yourself having to refactor all of your code, whereas if you have your own components, you will be able to more easily propagate those changes.

If you go this route, it's important to think about what kind of parameters each kind of form field will have, so you can avoid refactors later.

For example:

 function DropdownSelector({ label, value, onChange, options }) {
    return (
      <>
      <label>label</label>
      <Select value={value} onChange={onChange}>
        {options.map((o) => {
          return <option value={value}>{description}</option>
        }}
      </Select>
      </>);
  }

Then, as you figure out how you want your form to look, you can just tweak a small number of components until you get the desired look and behavior.

Upvotes: 1

svey
svey

Reputation: 115

Clean up:

    const handleChange = (e) => setClient({ ...client, [e.target.name] : e.target.value})
    
    return ( 
      <input
       name="lastName"
       onChange{handleChange}
      />
    );

Depending on your use case you may not even need to maintain state, just spread your client object into the form and your props to the appropriate inputs.

Alternatively useReducer is also an option

Upvotes: 1

momomo
momomo

Reputation: 319

Since you're using the useState hook. I would advice you to use the useReducer hook. useReducer works similarly to how redux works, and it looks cleaner in my opinion. Although performance wise there isn't a lot that is wrong with your example.

Upvotes: 1

Related Questions