Introducing hooks

Introducing hooks

React hooks — like components — are a type of plain old JavaScript function. For example, the useState() function is a hook:

let state = useState(1)

React comes with a whole set of built-in hooks, including useState(), useEffect(), useContext() and useRef(). Unlike components, these hook functions are designed to be called manually. And since they’re just functions, they can accept arguments and return values.

But what do all these hooks actually do? Well, you’ve already seen what useState() does — it makes it possible to use component state within function components. As for useEffect() it makes it possible to use effects within component functions. We’ll cover what exactly this means in the next lesson, but for now, I want to focus on one part of this:

Hooks let you use special features within component functions.

This is important to understand, as it leads to the first rule of hooks:

Hooks must only be called within function components, or within custom hook functions.

What this means is that you cannot use hooks within plain JavaScript functions. In fact, you also cannot use hooks within class components — which we’ll cover later on. But within a function component? You can use as many hooks as you’d like.

#Using multiple hooks

One of the fun things about hooks is that you don’t have to think about how you’ll fit all that state into a single object. Instead, if you have multiple stateful values… you just call useState() twice. Or three times, or however many times you need.

For example, this comes in handy when creating small forms. You just call useState() once for each field.

index.js
styles.css
import React, { useState } from 'react'
import ReactDOM from 'react-dom'

function App() {
  let [country, setCountry] = useState('')
  let [state, setState] = useState('')

  return (
    <form>
      <label>
        <span className="label">Country</span>
        <select
          value={country}
          onChange={event => setCountry(event.target.value)}>
          <option value="oceania">Oceania</option>
          <option value="other">Other</option>
        </select>
      </label>
      <label>
        <span className="label">State</span>
        <input
          value={state}
          onChange={event => setState(event.target.value)}
        />
      </label>
    </form>
  )
}

ReactDOM.render(<App />, document.getElementById('root'))
Build In Progress

    Being able to call useState() multiple times like this is super convenient, but it’s also a little odd. How does React know which useState() call should return which value? That’s a great question! I’m assuming you want to know the answer, because I sure as hell did when I first saw hooks.

    The secret is in the order in which you call useState().

    If you call useState() twice within the same component, then React will keep track of two values:

    1. The state for the first call to useState(), and
    2. The state for the second call to useState().

    To demonstrate this, let’s do a little experiment, and add a condition to this form. In particular, I’ve set up the form below to hide the state field when you pick a country other than the US. Probably because of tax or something.

    index.js
    styles.css
    import React, { useState } from 'react'
    import ReactDOM from 'react-dom'
    
    function App() {
      let [country, setCountry] = useState('other')
      if (country === "oceania") {
        let [state, setState] = useState('')
      }
    
      return (
        <form>
          <label>
            <span className="label">Country</span>
            <select
              value={country}
              onChange={event => setCountry(event.target.value)}>
              <option value="oceania">Oceania</option>
              <option value="other">Other</option>
            </select>
          </label>
          {country === "oceania" && (
            <label>
              <span className="label">State</span>
              <input
                value={state}
                onChange={event => setState(event.target.value)}
              />
            </label>
          )}
        </form>
      )
    }
    
    ReactDOM.render(<App />, document.getElementById('root'))
    Build In Progress

    Did you try changing the country? If not, go ahead — I’ll wait for you.


    What happened? It went kaboom. React detected that the number of hooks has changed, and it threw an error. And when you think about this, it actually makes a lot of sense.

    #Using state conditionally

    Imagine what would happen if there was a third call to useState(). In this case, if the second call to useState() suddenly disappeared, then the third useState() would become the second useState() — and then there’d be a glitch in the space-time continuum and then all hell would break loose. That’s why there’s a second rule of hooks:

    Hooks must only be called at the Top Level.

    What this means is that hooks cannot be called within:

    • Loops (e.g. for, while, do, etc.)
    • Conditions (e.g. if, switch, etc.)
    • Events handlers, callbacks, and other nested functions

    This rule ensures that hooks will always be executed in the same order, and that each call to useState(), useEffect(), and other order-dependent hooks behaves exactly as you’d expect it to. And that’s all well and good, but it does raise the question:

    If you can’t use hooks inside conditions, then how do you implement conditional logic in hook-based components?

    I’m going to let you try and figure this one out for yourself, as an exercise:

    Your task is to fix the above example so that it works as expected, with the state hidden for non-US countries.

    Once you’re done, you can check the solution above — and view my explanation below.

    While you must call each hook on each render, you don’t have to actually use the returned state!

    It’s perfectly acceptable to call hooks like useState() and then conditionally ignore the output in some situations. So if you need conditional logic — just place the conditions after the hooks. For an example, check the solution for the above demo.

    Okay, you’re getting the hang of this — so let’s take a look at one more hook while you’re on a roll.

    Progress to next section.