Nov 16, 2019 | 3 min read

Introduction to React Hooks

Imagine you have several components and you would want to reuse stateful logic between them without having to rewrite the components.

Traditionally, the typical way to share logic between components has been either to use patterns like higher order components and render props. But with these patterns, components have to be restructured in order to use them thereby contributing to complex code-bases and making them harder to maintain.

With the introduction of React Hooks, it is possible to extract stateful logic from a component so it can be tested independently and reused.

In this guide, I will be talking to you about how you can use Hooks to share stateful logic between your components.

So What Are Hooks?

Hooks are functions that let us hook into the React state and lifecycle features from function components.

Some of the basic built-in hooks in React are:

  • useState: the State Hook lets you add React state to function components.
  • useEffect: the Effect Hook lets you perform side effects in function components.

Managing State with useState

A quick recap on how you manage states using React class components.

class Counter extends React.Component { constructor(props) { super(props); this.state = { count: 0, }; } render() { return ( <div> <p>You clicked {this.state.count} times</p> <button onClick={() => this.setState({ count: this.state.count + 1 })}> Click me </button> </div> ); } }

With useState, we can achieve similar behavior in a functional component

import React, { useState } from "react"; function Counter() { // Defines state variable (count) and call (setCount) -- both of which can be named anything const [count, setCount] = useState(0); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}>Click me</button> </div> ); }

useState hook allows managing multiple states in the same component. e.g

const [age, setAge] = useState(10); const [count, setCount] = useState(0); const [name, setName] = useState("Solomon Ayoola");

Managing Side Effect with useEffect

Talking about side effects, we are referring to actions such as updating the DOM, making API calls, subscribing to event listeners, etc.

useEffect is similar to componentDidMount, componentDidUpdate, and componentWillUnmount lifecycle methods in React class.

With useEffect, React knows that you'd like to carry out a certain action after it's done rendering.

The example below shows how you can use useEffect to fetch data from an API and setting the response in the state

import React, { useState, useEffect } from "react"; function App() { const [users, setUsers] = useState([]); useEffect(() => { fetch("https://uinames.com/api/?amount=25") .then((response) => response.json()) .then((data) => { setUsers(data); }); }, []); // Render UI based on the state }

useEffect could be used to clean-up after running an effect, such as subscribing to an external resource—to prevent memory leaks.

It can also be used multiple times in the same component just like with useState

Custom Hooks

The best feature of Hooks is that you can easily share stateful logic across multiple components by creating custom Hook.

Custom hooks are normal javascript functions that can use other hooks and contain a common stateful logic that can be reused across multiple components.

In the example below, we will be making a custom useFetch Hook to fetch data from an API and then return data, loading state and error if the fetch fails.

import { useState, useEffect } from "react"; function useFetch(url) { const [data, setData] = useState([]); const [loading, setLoading] = useState(false); const [error, setError] = useState(""); useEffect(() => { setLoading(true); fetch(url) .then((response) => response.json()) .then((data) => { setLoading(false); setData(data); }) .catch((e) => { setLoading(false); setError(e); }); }, [url]); return { users, loading, error }; }

And below is how you can reuse the custom hook in your component:

import React from "react"; import useFetch from "./useFetch"; function App() { const { users, loading, error } = useFetch( "https://uinames.com/api/?amount=25" ); if (loading) { return <p>Loading...</p>; } if (error) { return <p>{error}</p>; } return ( <div className="users"> {users.map((user) => ( <div key={user.name} className="users__user"> <div className="users__meta"> <h1>{user.surname}</h1> <p>{user.gender}</p> </div> </div> ))} </div> ); }