custom hooks

ref – https://reactjs.org/docs/hooks-custom.html

Problem

Say we have two functional components FriendStatus and FriendListItem. They both make use of a common functionality: check to see if a friend is online.

FriendStatus.js

FriendListItem.js

Let’s extract this friend checking online functionality into a custom hook

A custom Hook is a JavaScript function whose name starts with ”use”. It may call other Hooks

In our case, we create useFriendStatus function as a custom hook. We use other hooks within it. Just make sure to only call other Hooks unconditionally at the top level of your custom Hook: Don’t call Hooks inside loops, conditions, or nested functions. Instead, always use Hooks at the top level of your React function. By following this rule, you ensure that Hooks are called in the same order each time a component renders. That’s what allows React to correctly preserve the state of Hooks between multiple useState and useEffect calls. (ref – https://reactjs.org/docs/hooks-rules.html)

Hook Rules

Say we have a function Form. It uses 4 hooks: useState, useEffect, useState, and finally useEffect again.
Notice our 2 useStates.

First one is that we create a state and initialize it to ‘Mary’.
It creates a new Hook object (with the initial state ‘Mary’), adds the object to the Hooks list, and return the array with the initial state and the setter/getter functions (i.e name, setName).

We then repeat to create another state and initialize it to ‘Poppins’.
It creates a new Hook object (with the initial state ‘Poppins’), adds the object to the Hooks list, and return the array with the initial state and the setter/getter functions (i.e surname, setSurname).

Hence we know which state ‘Mary’ and ‘Poppins’ belong to because when creating them, we create a new Hook object and add it to the Hooks list. Thus, React relies on the order in which Hooks are called. Our example works because the order of the Hook calls is the same on every render.

For each render, React goes down the hook list and executes.

As long as the order of the Hook calls is the same between renders, React can associate some local state with each of them. But what happens if we put a Hook call (for example, the persistForm effect) inside a condition?

The name !== ” condition is true on the first render, so we run this Hook. However, on the next render the user might clear the form, making the condition false. Now that we skip this Hook during rendering, the order of the Hook calls becomes different:

React wouldn’t know what to return for the second useState Hook call. React expected that the second Hook call in this component corresponds to the persistForm effect, just like during the previous render, but it doesn’t anymore. From that point, every next Hook call after the one we skipped would also shift by one, leading to bugs.

This is why Hooks must be called on the top level of our components. If we want to run an effect conditionally, we can put that condition inside our Hook:

Unlike a React component, a custom Hook doesn’t need to have a specific signature. We can decide what it takes as arguments, and what, if anything, it should return. In other words, it’s just like a normal function. Its name should always start with use so that you can tell at a glance that the rules of Hooks apply to it.

useFriendStatus

Using the custom hook

Now that we’ve extracted this logic to a useFriendStatus hook, we can just use it:

FriendStatus.js

FriendListItem.js

Since Hooks are functions, we can pass information between them

To illustrate this, we’ll use another component from our hypothetical chat example. This is a chat message recipient picker that displays whether the currently selected friend is online: