React/ReactJS: Custom Hooks

Before Hooks, Render Props and Higher-Order Components are the two ways state is shared between components in React.

However, logic between two functions is better shared by extracting it to a third function. It works both for Function Components and Hooks as they are functions.

A Custom Hook is a JavaScript function whose name starts with ”use” and that may call other Hooks.

Below we construct a simple Custom Hook called usePi.js which uses the two built-in Hooks useState and useEffect and returns the value of π. It is, of course, practically a useless example.

Following the Rules of Hook, we call the existing Hooks at the top level even inside a Custom Hook. They are not to be called inside loops, conditions, or nested functions. They are also not to be called from regular JavaScript functions.

We pass an empty array [] as the second argument to useEffect, which serves the purpose of componentDidMount(), signalling that it is called only once after the component is mounted.

				
				// usePi.js
				import { useState, useEffect } from "react";
				const usePi = () => {
				  const [pi, setPi] = useState(0);
				   useEffect(() => {
				 	setPi(Math.PI);
				  }, []);
				  return pi;
				}
				export default usePi;
				
			

We import and use the above Custom Hook usePi.js inside a function component:

				
				import React from "react";
				import usePi from "./usePi";
				const valueOfPi = (props) => {
				  const pi = usePi();
				  return (
				    <div>
				      The value of pi is {pi}
				    </div>
				  );
				}
				export default valueOfPi;
				
			

Practically, we need not go about in such roundabout manner (to the extent of creating Custom Hooks) just to get the value of π.

We pick a much useful example in the section below.

The useFetch() Custom Hook

Now we will take up a different example, something useful practically compared to the first above. We build a Custom Hook to fetch some data on load of a component using the Fetch API. We pass an empty array [] to useEffect() indicating that it is a one-time load on initiation of the component.

					
					// useFetch.js
					import  { useState, useEffect } from "react";
					const useFetch = (url) => {
					  const [data, setData] = useState([]);
					  useEffect(() => {
					    fetch(url)
					      .then(response => response.json())
					      .then(data => setData(data));
					  }, []);
					  return data;
					}
					export default useFetch;
					
				

Now we will use the above Custom Hook we just created in another component. We fetch a random Chuck Norris joke by passing the free URL https://api.chucknorris.io/jokes/random into our usefetch() custom hook, which retrieves the following JSON:

					
					{
					    "categories": [
					        "dev"
					    ],
					    "created_at": "2024-01-05 13:42:19.324003",
					    "icon_url": "https://assets.chucknorris.host/img/avatar/chuck-norris.png",
					    "id": "fivi0z5lt8gp_6vt3cc8pw",
					    "updated_at": "2024-01-05 13:42:19.324003",
					    "url": "https://api.chucknorris.io/jokes/fivi0z5lt8gp_6vt3cc8pw",
					    "value": "If Chuck Norris writes code with bugs, the bugs fix themselves."
					}
					
				

And inside another component, we just render the icon_url and value fields.

					
					import React from "react";
					import useFetch from "./useFetch";
					const ChuckNorrisJoke = () => {
					  const data = useFetch("https://api.chucknorris.io/jokes/random");
					  return (
					    <div>
					      <img src={data.icon_url}/>
					      <p>{data.value}</p>
					    </div>
					  );
					}
					export default ChuckNorrisJoke;
					
				

The output is as follows.

NOTE. If you refresh the page, the joke will change.