Skip to content

Joey Ng'ethe

Introduction to React Hooks

React2 min read

A couple of weeks ago, as of the writing of this article, Dan Abramov took onto the stage to introduce React Hooks to the React community at React Conf 2018 in Henderson, Nevada. And at first glance, I was encouraged for as much as it introduces new API, it makes it easy for React developers to use the same features we have been using for years but now, more efficiently and elegantly. Most importantly, they don’t introduce breaking changes and are 100% backward compatible.

Dan Abramov at React Conf 2018

So what exactly are Hooks?

Hooks are a new feature proposal that lets you use state and other React features without writing a class. That’s right! You can write a function component and have access to its local state and also its lifecycle methods 🎉🎉

They are currently in React v16.7.0-alpha; You can follow the discussions and developments here.

The motivation of introducing Hooks

Any API introductions should be backed up with motivation or reason as to “why,” and this is no different. Below are some of the reasons why;

  • Currently, it’s hard to use stateful logic between components: If you have been using React for a while, you’d say that HOC and Render Props patterns do solve this problem, which is valid to some extent, but they do introduce some constraints and other pain points. For example, these patterns require you to restructure your components before you use them, and this can be cumbersome and make your code hard to follow. These patterns also introduce the “wrapper hell” where your components are wrapped in a long nested DOM. While this can be filtered out using React Dev tools, it’s not ideal.
  • Intricate components can be hard to understand: React components usually start small, but with time they become complicated and big, which makes them hard to follow and understand. Each lifecycle method can have a bunch of unrelated logic that makes async calls to fetch some data, sets up event listeners, and cleans up these events when the component un-mounts. Based on the complexity of your application, all these can be a bit messy and hard to follow.
  • Classes are confusing to both people and computers: Classes can be confusing to new React devs, and it’s one of the hindrances that some people are reluctant to learn React. For you to use classes, you have to understand how this works, and you have to bind your methods in the constructor, which can be a bit repetitive. Classes don’t get minified very well by dev tools, and they make hot reloading flaky and unreliable.

We think Hooks are our best shot at solving all of these problems. Hooks let us organize the logic inside a component into reusable isolated units. — Dan Abramov

Show me some code already!

Hooks are categorized into two categories: Basic and Additional Hooks. I am going to demo useState and useEffect. Be sure to check the others out, for they will come in hand in your development work. I will write about the other hooks in subsequent series' of articles.

State Hook

We'll create a controlled form input for setting a name.

1import React from 'react';
2
3const Counter = () => {
4 const [name, setName] = React.useState('John Doe');
5
6 const handleNameChange = (e) => setName(e.target.value);
7
8 return (
9 <div>
10 <form>
11 <label htmlFor="name">Your name</label>
12 <input id="name" value={name} onChange={handleNameChange} />
13 </form>
14 <hr />
15 <p>{`Your name is ${name}`}</p>
16 </div>
17 );
18};

Draw your attention on the highlighted line of code which, shows the signature of the useState hook. It returns a destructured pair, the current value, and an updater function that lets you update that specific value. In this case, we have name and setName, which holds the current name value and updates the name, respectively. The useState hook takes an optional value, which will be its initial value. In this case, we have John Doe.

We then data-bind the input to the name value. We achieve this by passing the name to the value of the input field along with a synthetic-event onChange handler, as depicted below.

1<input id="name" value={name} onChange={handleNameChange} />

The handleNameChange function passes the value of the input to the setName updater function, which completes our two-way data-binding.

Below are some points to note when using useState;

  • You can declare multiple useStates in your application as you'd like
  • You can also pass a function to useState to lazy load a value; say reading a value from localStorage

useEffect

This hook is used for making side effects like making API calls, subscriptions or manually changing the DOM in React components. This is the equivalent of componentDidMount, componentDidUpdate and componentWillUnmount but in a single unified API.

To demonstrate this, we will make an API call to the Star Wars API.

useEffect
1import React, { useState, useEffect } from 'react';
2
3export default function App() {
4 const [data, setData] = useState([]);
5 const [loading, setLoading] = useState(false);
6
7 const controller = new AbortController();
8 const signal = controller.signal;
9
10 const fetchData = () => {
11 setLoading(true);
12 fetch('https://swapi.dev/api/people/1/', { signal })
13 .then((res) => res.json())
14 .then((data) => {
15 setLoading(false);
16 setData(data);
17 })
18 .catch((error) => {
19 setLoading(false);
20 console.error(error);
21 });
22 };
23
24 useEffect(() => {
25 fetchData();
26 return () => {
27 controller.abort();
28 };
29 }, []);
30
31 return (
32 <div className="App">
33 <h1>Hello CodeSandbox</h1>
34 <h2>Start editing to see some magic happen!</h2>
35 {loading && <p>Loading...</p>}
36 {!loading && <pre>{JSON.stringify(data, null, 2)}</pre>}
37 </div>
38 );
39}

Draw your attention to the highlighted lines of code. We are calling the useEffect hook and passing it a callback function. The callback calls fetchData function which pulls data from an API hence causing a side effect.

The component might unMount before this function is settled, hence we return a clean up function which cancels the request using the AbortController API to avoid any memory leaks. We finally pass a dependancy for this function in our case, an empty array. This tells React to call this function only once on Render. This is the equivallent of componentDidMount when using class Components. If we didn't pass any dependacies, the function would be called over and over infinitely.

Below is the signature of useEffect;

useEffect
1useEffect(
2 () => {
3 /* Make side effects e.g;
4 - subscriptions
5 - API calls
6 - DOM manipulation e.t.c
7
8 */
9 return () => {
10 /* do some cleanup work like;
11 - cancelling any unsettled network requests
12 - unsubscribing to any subscriptions e.t.c
13 */
14 };
15 },
16 [
17 /*dependacies*/
18 ]
19);

Hook Rules

Hooks are Javascript functions and they come with some rules;

  • Only call them at the top leavel. Don't call them inside loops, conditional statements or nested functions.
  • Only call Hooks in React function components. Don't call them in normal JS functions. The only other place you can call them is in your custom hooks.

Why should I use Hooks?

Hooks allow you to write more cleaner code with less boilerplate. Below are some reactions from the community.

What next?

Hooks are awesome and they let you enjoy the same API's we've been using, only this time, with a little less code.

You can take this further by writting your custom hooks that you can re-use on your code base. There are more hooks that I haven't touched on here. You can learn more about them on here.