React Design Pattern Part 1: Custom Hook

Md Sajjad Hosen Noyon

30 July, 2024

The Custom Hook pattern in React is a technique that boosts efficiency and productivity. These JavaScript functions, which use the hooks provided by React (such as useState, useEffect, useContext, etc.), allow you to share logic between components, effectively encapsulating and reusing logic.

When to use it?
  • Custom Hooks are useful when you need to share logic between React components without resorting to code duplication. For example, when you have multiple components that need to fetch data from an API, you can create a custom hook for the fetch logic and use it in all these components. 
  • To abstract the complex logic of a component and keep it more readable and easier to maintain.
  • Need to modularize the logic of a component to facilitate its unit testing.
Example:
				
					const useFetch = (url) => {
 // required logic

 return { x, y, ...rest };
}

const GetPosts = () => {
 const allPosts = useFetch('https://example.com/api/post');

 return (
   <div>
     {/* required JSX */}
   </div>
 );
}

const GetComments = () => {
 const allComments = useFetch('https://example.com/api/data');

 return (
   <div>
     {/* required JSX */}
   </div>
 );
}
				
			

In the above implementation, useFetch is a custom hook that returns an object. GetPost and GetComments are both components that use the useFetch custom hook separately.

Custom Hooks let you share stateful logic, not state itself

Below example, when you turned the network on and off, both components updated together. However, it’s wrong to think they share a single isOnline state variable. Look at this code:

				
					function StatusBar() {
 const isOnline = useOnlineStatus();
 // ...
}

function SaveButton() {
 const isOnline = useOnlineStatus();
 // ...
}
				
			

Though both StatusBar and SaveButton components use the OnlineStatus hook individually, each call to a hook is entirely independent of every other call to the same hook.

Advantages
  • Promotes code reuse by encapsulating common logic in separate functions.
  • Separating logic from the component facilitates code composition(combining smaller, independent components to create complex UIs) and readability.
  • Improves testability by enabling more specific and focused unit tests on the logic encapsulated in Custom Hooks.
Disadvantages
  • It’s essential to be mindful of creating many custom hooks, as this can lead to unnecessary complexity if not used with caution and a clear purpose.
  • A solid understanding of React and Hooks concepts is required for proper implementation.
				
					// Wrong approach
const useSorted = (items) => {
 return items.slice().sort();
} // Don’t use a custom hook if you do not require any hook inside it.

//Correct Approach
const getSorted = (items) => {
 return items.slice().sort();
} // Just use a function

const ProductItems = (items) => {
 const sortedProductItems = useSorted(items); // Wrong
 const sortedProductItems = getSorted(items); // Correct
 return (
   <div>
     required JSX
   </div>
 );
}
				
			
A Custom Hook Example:

Here is an example of a Custom Hook that performs a generic HTTP request using React. This Hook handles the logic to make the request and the load status, data, and errors.

				
					import { useState, useEffect } from 'react';
import axios, { AxiosResponse, AxiosError } from 'axios';

// Custom Hook
function useFetch {
 const [data, setData] = useState(null);
 const [loading, setLoading] = useState(true);
 const [error, setError] = useState(null);

 useEffect(() => {
   const fetchData = async () => {
     try {
       const response = await axios.get(url);
       setData(response.data);
     } catch (error) {
       setError(error);
     } finally {
       setLoading(false);
     }
   };

   fetchData();

   return () => {};
 }, [url]);

 return { data, loading, error };
}

function ExampleComponent() {
 const { data } = useFetch('https://example.com/api/data');

 if (!data) {
   return <div>No data found.</div>;
 }

 return (
   <div>
     {/* Rendering of the obtained data */}
   </div>
 );
}

export default ExampleComponent;
				
			

In this example, the Custom Hook useFetch takes a URL as an argument and performs a GET request using Axios. It manages the load status, data, and errors returning an object with this information.

 

The ExampleComponent component uses the Custom Hook useFetch to fetch data from an API and render it in the user interface. Depending on the request’s status, a load indicator, an error message, or the fetched data are displayed.

Recap about Custom hook
  • Custom hooks let you share logic between components.
  • It must be named starting with use followed by a capital letter.
  • It only shares stateful logic, not the state itself.
  • You can pass reactive values from one hook to another and stay up-to-date.
  • All hooks re-run every time your component re-renders.
  • The code of custom hook should be pure, like your component’s code.
  • Don’t create custom hook like useMount. Keep their purpose specific.
Reference
  • https://react.dev/learn/reusing-logic-with-custom-hooks#should-all-functions-called-during-rendering-start-with-the-use-prefix
Md Sajjad Hosen Noyon

30 July, 2024