Simplify controlled components with React hooks
Tags: react, webdev, javascript
Published: 2019-08-13 22:40:20

While working with react it is almost inevitable to come across controlled components. A controlled component is a react component that controls the values of input elements in a form using `setState()`.

Before the new hooks API was introduced, you could only use class components for this purpose since they are the only ones that can store state and have access to the setState API. But now with the introduction of hooks, we can finally handle state changes in any component (functional or class) greatly simplifying writing controlled components.

Here is an example of a controlled component using the traditional approach with a class component:

*RegularControlledComponent.js*

```react
import React, { Component } from 'react'

export class RegularControlledComponent extends Component {
  state = {
    username: '',
    password: '',
  }

  handleUsernameChange = (e) => this.setState({
    username: e.currentTarget.value,
  })

  handlePasswordChange = (e) => this.setState({
    password: e.currentTarget.value,
  })

  render() {
    return (
      <form>
        <div>
          <label>Username:</label>
          <input type="text" onChange={this.handleUsernameChange} />
        </div>
        <div>
          <label>Password:</label>
          <input type="text" onChange={this.handlePasswordChange} />
        </div>
        <input type="submit" />
      </form>
    )
  }
}

```

At first, it may seem there is nothing wrong with it, but what would happen if instead of two input fields we had 5 or 10? we will need 10 `handleSomeInputFieldChange` function handlers. 
THIS APPROACH IS NOT SCALABLE

Let's rewrite our component to control the input fields using hooks:

*ControlledComponentWithHooks.js*

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

export const ControlledComponentWithHooks = () => {
  const [input, setInput] = useState({})

  const handleInputChange = (e) => setInput({
    ...input,
    [e.currentTarget.name]: e.currentTarget.value
  })

  return (
    <form>
      <div>
        <label>Username:</label>
        <input type="text" name="username" onChange={handleInputChange} />
      </div>
      <div>
        <label>Password:</label>
        <input type="text" name="password" onChange={handleInputChange} />
      </div>
      <input type="submit" />
    </form>
  )
}


```
The first change to notice is that our component is now a function, with the introduction of the `useState` hook we are no longer obligated to convert our functional components into class components when we want to use local state.

Secondly, we are now programmatically setting values to our state variables, the way we accomplished this is by adding a new `name` attribute to the input fields in lines `17` and `25`. The magic happens in line `8`: `[e.currentTarget.name]: e.currentTarget.value`
here we are using that name as the property value for our state object and assigning the input value to it.

This approach is scalable since it doesn't matter the number of input fields in this form, they will all use the same `handleInputChange` and the local state will be updated accordingly. **Beautiful**

Now! let's make this even better by abstracting the hook into its own file to make it reusable.

*useInputChange.js*

```react
import { useState } from 'react'

export const useInputChange = () => {
  const [input, setInput] = useState({})

  const handleInputChange = (e) => setInput({
    ...input,
    [e.currentTarget.name]: e.currentTarget.value
  })

  return [input, handleInputChange]
}
```

Now our functional component `ControlledComponentWithHooks.js` just have to import and use the new hook.

```react
import React from 'react'
import { useInputChange } from './useInputChange'

export const ControlledComponentWithHooks = () => {
  const [input, handleInputChange] = useInputChange()
 
  return (
    <form>
      <div>
        <label>Username:</label>
        <input type="text" name="username" onChange={handleInputChange} />
      </div>
      <div>
        <label>Password:</label>
        <input type="text" name="password" onChange={handleInputChange} />
      </div>
      <input type="submit" />
    </form>
  )
}

```

Isn't it cool? all the setState and input handlers boilerplate have been completely removed from our component. with our new hook creating controlled components is simple as it gets, making our component more readable and focusing on the specific business logic that it was created for.

# Conclusion

Just like `HOCs` and `render prop`, hooks allow us to reuse logic in our components. We can leverage this to do all sort of abstractions.

All the source code from this article can be found in the following Repl.it:
https://repl.it/@StanleyJovel/Controlled-Components-with-Hooks