React Hooks I: An In-depth Look

State in functional components

Simon Karanja
3 min readDec 4, 2020
Hooking on the subway

React hooks is a god’s sent feature that was introduced in React 16.8. In old days, if your component did not handle any local state, then you would normally write it as a stateless functional component. Simple and elegant. Now the problem came if you decided to introduce state to the said component. You would need to refactor your functional component to a class based component to enjoy the state & the lifecycles.

Now with the Hook API, you can easily use state, introduce side effects and so much more. React provides a handful of hooks, but the most widely used are:-

  • useState()
  • useRef()
  • useEffect()
  • useReducer()

We will have a look at these hooks shortly, but before that, notice how these hooks are conveniently named, useState() for state, useEffect() for side effects etc. It’s not a mistake as we will come see when creating custom hooks and exploring rules of hooks. In this mini series we will look at each more deeply, starting with the easiest and perhaps the most popular, useState().

useState()

With a single line of code, you can introduce state to a functional component (How 😎… I know). That’s how powerful hook API is. The general syntax of creating a state hook is as follows:-

const [value, setValueAndReRender] = React.useState('initial value')

Notice the ES6 destructuring syntax. The useState() hooks takes in an initial state and returns a pair, i.e. the current state and a function to update the state. That would be value and setValueAndReRender in our case above. As with any state change, our updater function will cause a re-render. Will look at another hook useRef() later on the series, that does not cause a re-render.

As an example, let’s create a simple state with a timer variable initialized to zero.

const [timer, setTimer] = React.useState(0)

Note: useState() can only be used in functional components

Now, this is how our complete timer might look like as a functional component.

Now, let’s look at how it would have looked as a class based component.

Notice how clean & elegant the hook version of our component looks. For starters, no convoluted this keyword and no verbose boilerplate code associated with class components.

What if I have more state variables?

It’s usual to have more that a couple of state variables, in that case you could of course create an object to hold all your state. Let see how this would look. Suppose we have a Tracker component that tracks weather & GPS location of say an IOT device.

This is how our initial state looks:-

{lat: 0, lon:0, temp: 0, wind: 0}

Let simulate a movement of our IOT device along the Equator (lat: 0.0) by clicking the Update Tracker button. Now our state looks like this:-

{lon:-121.45954331}

Notice we have lost the lat, wind & temp state variable. It’s no mistake. Unlike this.setState in a class, updating a state variable always replaces it instead of merging it.

Important: The updater function of the useState() replaces the corresponding state.

To overcome this we could rewrite our handleUpdates() as:-

This merges the previous and the new state manually. Running this, we now have the expected output:-

{lat:0, lon:150.52565993, temp:0, wind:0}

The above is one way of approaching this, however the reacts docs recommends:-

We recommend to split state into multiple state variables based on which values tend to change together.

Let’s look at this approach. Now let refactor our state into location and weather state variables. Our component looks likes this:-

Although verbose, we now have a clean & clearer state variables, with decoupled update handlers and no need of merging the state. Should you wish to introduce, say hooks later on, then it’s simple as the state is now partitioned into smaller, meaningful & manageable values.

What of a very complex state?

The only downside of useState() you might argue is managing complex state. Although you can use as many useState() hooks or put your state variables into an object as we did above, there’s a far superior approach that borrows heavily from Redux, the useReducer() hook. We will explore this later in our mini series.

That’s all for the first part, we will look at useEffect() in the next series.

Adios!! ✌️

--

--

Simon Karanja

Aspiring comedian and a lover of anything with .js extension