React - How to convert to hooks - state

I’ve recently started a new project using React, and I began in my usual manner of mostly using​ class components. But, since I’ve decided to embrace Hooks, I’ve tried to convert my current classes into functional components.

Firstly, I tackled state, one of the key functionalities of React. Hooks make state much easier to declare by removing some of the previous boilerplates, including a constructor, which (in my opinion) makes the code much more readable.

The below code considers an input field that is used to dynamically filter an array by storing keystrokes into the component’s state:

Pre-hooks code

This code uses the Class component:

export class SearchResults extends Component {
  constructor(props) {
    super.props;
 
    this.state = {
      inputFilter: ''
    };
  }

  itemFilter = (event) => {
    this.setState( state: {
      inputFilter: event.target.value. toLowerCase()
      });
  };
...

Hooks

Incorporating hooks in the code:

export const SearchResults = () => {

  const [inputFilter, setInputFilter] = useState(initialState: '');
  
  const itemFilter = (event) => setInputFilter(event.target.value.toLowerCase());

Note: for this to work we need to import { useState } from React.

Both of the codes above are dependent on the following input field:

<input type="search"
  className="form-control"
  placeholder={'Type here to filter...'}
  onChange={itemFilter}
/>

What’s the difference?

Hooks allow you to give a functional component its own state, something that wasn’t previously available.

As noted, Hooks remove the previous boilerplate we’d need to create a component’s state. Now, we can define an inputFilter entry by using the useState method and passing it in a default value (here an empty string). Note, however, that we first need to import the useState hook from the React library:

import React, { useState } from 'react';

When setting up our inputFilter state entry, we also need to define a way to update that entry (here we’ve called this setInputFilter). This is a function that is used to update our state.

In the code above, the initial state of inputFilter is set to an empty string. When a user starts typing into the input field, the function itemFilter is called. This then passes the typing event to the itemFilter function, as a parameter (defined as event), which is then translated into the actual characters typed by the user (denoted by event.target.value.toLowerCase())

svg viewer

So, our keystrokes have been passed to the function setInputFilter, and those characters are then used to set the value of our inputFilter element in our state. This is all we need to do to create and update our Hooks based state!

Other things to note

This example shows how to update a state that only has one value. However, an important scenario to consider is when the state element is an object or an array. With Hooks, using the updating function, we’ll be overwriting our state element rather than appending to it as would have been the case without Hooks. So, in this instance, if we want to update only a portion of an object or an array, we’d need to use the ES6 spread operator to maintain those other pieces of information.

So, in our application, if our state object is modified to include two separate filters, filter1 and filter2 like below, and we changed our setInputFilter function to update only filter1

const [inputFilter, setInputFilter] = useState (initialState: {
  filter1: '',
  filter2: ''
});

const itemFilter = (event) => setInputFilter(value: {filter1: event.target.value.toLowerCase()});

…then this overwrites our previous value for filter2

> {filter1: "", filter2: ""}
> {filter1: "a"}

…and it’s lost. This can be very dangerous for losing elements of your state!

This wouldn’t have been the case without using hooks as setState({}). Only amend the variable you wish to update.

Instead, we use the spread operator to maintain other pieces of state, only updating the value that we want:

const [inputFilter, setInputFilter] = useState (initialState: {
  filter1: '',
  filter2: ''
});

const itemFilter = (event) => setInputFilter(
  value: {...inputFilter, 
    filter1: event.target.value.toLowerCase()
  });

Now, both pieces of the state are maintained:

> {filter1: "", filter2: ""}
> {filter1: "a"}

Conclusion

As previously noted, I believe Hooks have lead to more readable code for creating and updating state. This makes it a lot more intuitive for new developers and also reduces the pain that some may have faced when their previously stateless functional component requires state.

But be careful when updating objects or arrays. Use the spread operator to maintain pieces of state that you didn’t want to alter!

Free Resources

Attributions:
  1. undefined by undefined