How I think when I write a React component. 🤔
Tags: javascript, react
Published: 2019-11-02 11:13:16

This is going to be a short demonstration on how I usually think when I write a React component.

So, let's say that I want to create a form component. 

I don't care what fields the form will have at the moment.

```jsx
import React from 'react';

function Form() {
  return (
    <form>
      {/*  */}
    </form>
  ) 
}

export default Form;
```

I want to add a `firstName` field.

```jsx
import React, { useState }  from 'react';

function Form() {
  const [firstName, setFirstName] = useState('');

  const handleFirstNameChange = ({ target }) => {
    setFirstName(target.value);
  }

  return (
    <form>
      <div>
        <label htmlFor="firstName">First name</label>
        <div>
          <input
            id="firstName"
            onChange={handleFirstNameChange}
            type="text"
            value={firstName}
          />
        </div>
      </div>
    </form>
  ) 
}

export default Form;
```

Looking good. 😎

I want to add a `lastName` field.

```jsx
import React, { useState }  from 'react';

function Form() {
  const [firstName, setFirstName] = useState('');
  const [lastName, setLastName] = useState('');

  const handleFirstNameChange = ({ target })  => {
    setFirstName(target.value);
  }

  const handleLastNameChange = ({ target }) => {
    setLastName(target.value);
  }

  return (
    <form>
      <div>
        <label htmlFor="firstName">First name</label>
        <div>
          <input
            id="firstName"
            onChange={handleFirstNameChange}
            type="text"
            value={firstName}
          />
        </div>
      </div>

      <div>
        <label htmlFor="lastName">Last name</label>
        <div>
          <input
            id="lastName"
            onChange={handleLastNameChange}
            type="text"
            value={lastName}
          />
        </div>
      </div>
    </form>
  ) 
}

export default Form;
```

Adding that second field was way easier.

I used my `copy paste` powers.

I want to add an `email` field.

I shall use my powers once again. 🐱‍🏍

```jsx
import React, { useState }  from 'react';

function Form() {
  const [firstName, setFirstName] = useState('');
  const [lastName, setLastName] = useState('');
  const [email, setEmail] = useState('');

  const handleFirstNameChange = ({ target }) => {
    setFirstName(target.value);
  }

  const handleLastNameChange = ({ target }) => {
    setLastName(target.value);
  }

  const handleEmailChange = ({ target }) => {
    setEmail(target.value);
  }

  return (
    <form>
      <div>
        <label htmlFor="firstName">First name</label>
        <div>
          <input
            id="firstName"
            onChange={handleFirstNameChange}
            type="text"
            value={firstName}
          />
        </div>
      </div>

      <div>
        <label htmlFor="lastName">Last name</label>
        <div>
          <input
            id="lastName"
            onChange={handleLastNameChange}
            type="text"
            value={lastName}
          />
        </div>
      </div>

      <div>
        <label htmlFor="email">Email</label>
        <div>
          <input
            id="email"
            onChange={handleEmailChange}
            type="email"
            value={email}
          />
        </div>
      </div>
    </form>
  ) 
}

export default Form;
```

...

Then I want to add a `password` field.

...

Then I want to add another field.

...

...

STOP! 🤚

Every new field translates into these three changes:

1. Adding a state and set state action for the field
2. Adding a new event handler for the input
3. Adding the HTML for the field

It's time for me now to use my real powers.

I'll attempt to decrease the number of changes that occur.

I don't want to add a new event handler for every input.

The only thing that changes in every event handler is the action that gets called.

I'll pass that as an argument.

```jsx
import React, { useState }  from 'react';

function Form() {
  const [firstName, setFirstName] = useState('');
  const [lastName, setLastName] = useState('');
  const [email, setEmail] = useState('');

  const handleChange = setStateAction => ({ target }) => {
    setStateAction(target.value);
  }

  return (
    <form>
      <div>
        <label htmlFor="firstName">First name</label>
        <div>
          <input
            id="firstName"
            onChange={handleChange(setFirstName)}
            type="text"
            value={firstName}
          />
        </div>
      </div>

      <div>
        <label htmlFor="lastName">Last name</label>
        <div>
          <input
            id="lastName"
            onChange={handleChange(setLastName)}
            type="text"
            value={lastName}
          />
        </div>
      </div>

      <div>
        <label htmlFor="email">Email</label>
        <div>
          <input
            id="email"
            onChange={handleChange(setEmail)}
            type="email"
            value={email}
          />
        </div>
      </div>
    </form>
  ) 
}

export default Form;
```

I'll try to add that `password` field now.

```jsx
import React, { useState }  from 'react';

function Form() {
  const [firstName, setFirstName] = useState('');
  const [lastName, setLastName] = useState('');
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');

  const handleChange = setStateAction => ({ target }) => {
    setStateAction(target.value);
  }

  return (
    <form>
      <div>
        <label htmlFor="firstName">First name</label>
        <div>
          <input
            id="firstName"
            onChange={handleChange(setFirstName)}
            type="text"
            value={firstName}
          />
        </div>
      </div>

      <div>
        <label htmlFor="lastName">Last name</label>
        <div>
          <input
            id="lastName"
            onChange={handleChange(setLastName)}
            type="text"
            value={lastName}
          />
        </div>
      </div>

      <div>
        <label htmlFor="email">Email</label>
        <div>
          <input
            id="email"
            onChange={handleChange(setEmail)}
            type="email"
            value={email}
          />
        </div>
      </div>

      <div>
        <label htmlFor="password">Password</label>
        <div>
          <input
            id="password"
            onChange={handleChange(setPassword)}
            type="password"
            value={password}
          />
        </div>
      </div>
    </form>
  ) 
}

export default Form;
```

OK, looking a bit better.

I think I can cross that out from the list.

1. Adding a state and set state action for the field
2. ~~Adding a new event handler for the input~~
3. Adding the HTML for the field

I don't want to add a new state and set state action for every field.

I'll update the event handler since I'll use one set state action.

I'll also add a name property to those inputs.

```jsx
import React, { useState }  from 'react';

function Form() {
  const [values, setValues] = useState({});

  const handleChange = ({ target }) => {   
    setValues(prev => ({ ...prev, [target.name]: target.value })); 
  }
  
  return (
    <form>
      <div>
        <label htmlFor="firstName">First name</label>
        <div>
          <input
            id="firstName"
            name="firstName"
            onChange={handleChange}
            type="text"
            value={values.firstName || ''}
          />
        </div>
      </div>

      <div>
        <label htmlFor="lastName">Last name</label>
        <div>
          <input
            id="lastName"
            name="lastName"
            onChange={handleChange}
            type="text"
            value={values.lastName || ''}
          />
        </div>
      </div>

      <div>
        <label htmlFor="email">Email</label>
        <div>
          <input
            id="email"
            name="email"
            onChange={handleChange}
            type="email"
            value={values.email || ''}
          />
        </div>
      </div>

      <div>
        <label htmlFor="password">Password</label>
        <div>
          <input
            id="password"
            name="password"
            onChange={handleChange}
            type="password"
            value={values.password || ''}
          />
        </div>
      </div>
    </form>
  ) 
}

export default Form;
```

OK, I'll cross that one out as well.

1. ~~Adding a state and set state action for the field~~
2. ~~Adding a new event handler for the input~~
3. Adding the HTML for the field

This is me going berserk now.

```jsx
import React, { useState }  from 'react';

const fields = [
  {
    id: 'firstName',
    label: 'First name',
    name: 'firstName',
    type: 'text'
  },
  {
    id: 'lastName',
    label: 'Last name',
    name: 'lastName',
    type: 'text'
  },
  {
    id: 'email',
    label: 'Email',
    name: 'email',
    type: 'email'
  },
  {
    id: 'password',
    label: 'Password',
    name: 'password',
    type: 'password'
  }
];

function Form() {
  const [values, setValues] = useState({});

  const handleChange = ({ target }) => {   
    setValues(prev => ({ ...prev, [target.name]: target.value })); 
  }

  return (
    <form>
      {fields.map(({ id, label, name, type }, index) => (
        <div key={index}>
          <label htmlFor={id}>{label}</label>
          <div>
            <input
              id={id}
              name={name}
              onChange={handleChange}
              type={type}
              value={values[name] || ''}
            />
          </div>
        </div>
      ))}
    </form>
  ) 
}

export default Form;
```

Well, now when I want to add a field, I just add one in my fields array. 😁

1. ~~Adding a state and set state action for the field~~
2. ~~Adding a new event handler for the input~~
3. ~~Adding the HTML for the field~~

What do you think?