Introduction
Since version 16.8, React has revolutionized the way we handle component logic by introducing custom hooks. These powerful functions allow us to encapsulate and reuse logic in an efficient and elegant way. For those of you who are not familiar with hooks in React, here is a brief explanation.
The hooks in React are JavaScript functions whose name begins with use and can invoke other React hooks within them. They allow to handle states and side effects in functional components, eliminating the need to use classes. A custom hook is a custom hook that we create ourselves, extending the basic functionality offered by React.
In this post, we will explore how to create our own custom hooks with practical and useful examples for different scenarios, showing how they can improve the organization and reuse of logic in our React applications.
Advantages of Using React Custom Hooks
- ReuseThey allow complex logic to be reused in different components without duplication of code.
- Cleaner Code: They help keep components cleaner and more UI-focused, delegating logic to hooks.
- MaintainabilityThey facilitate code maintenance by having decoupled and well-organized logic.
- AbstractionThey allow the creation of abstractions of complex logic, making the components easier to understand and test.
- FlexibilityThey can accept parameters and return customized results, allowing you to create flexible and adaptable solutions for different needs.

Some examples of custom hooks
useFetch: making http requests fast and easy
One of my favorite hooks is the one that allows me to make http calls in an easy and simple way, since we usually do this process over and over again. I usually call this hook useFetch
import { useState, useEffect } from 'react';
const useFetch = (url) => {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchData = async () => {
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error('Network response was not ok');
}
const result = await response.json();
setData(result);
} catch (error) {
setError(error);
} finally {
setLoading(false);
}
};
fetchData();
}, [url]);
return { data, loading, error };
}
export default useFetch;
As you can see, making use of React's hook useState, I can save those values that I always deal with when I make an API call, the data, the errors and whether or not it is making the call.
If we have this hook in a separate file, its use would be as simple as writing the following in our code.
const { data, loading, error } = useFetch('https://api.example.com/data');Thus, we could use it in an example like the following one
import useFetch from './useFetch';
const App = () => {
const { data, loading, error } = useFetch('https://api.example.com/data');
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
return (
<div>
<h1>Data:</h1>
<pre>{JSON.stringify(data, null, 2)}</pre>
</div>
);
}
export default App;
useLocalStorage: save data in the browser's local storage
Another hook that I use frequently is one that facilitates the manipulation of the browser's local storage. Although it is not an operation that is performed every day, having a hook like the one I show you below can save you a lot of time and effort when you need to interact with the local storage.
import { useState } from 'react';
const useLocalStorage = (key, initialValue) => {
const [storedValue, setStoredValue] = useState(() => {
try {
const item = window.localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch (error) {
console.error(error);
return initialValue;
}
});
const setValue = value => {
try {
const valueToStore = value instanceof Function ? value(storedValue) : value;
setStoredValue(valueToStore);
window.localStorage.setItem(key, JSON.stringify(valueToStore));
} catch (error) {
console.error(error);
}
};
return [storedValue, setValue];
}
export default useLocalStorage;
As you can see, this hook is somewhat more complex so it is always useful to have it at hand and not have to worry every time to go to the documentation to see how to work with the locale.
A use of this hook, although very simple, makes us see how easy it is to use.
import useLocalStorage from './useLocalStorage';
const App = () => {
const [name, setName] = useLocalStorage('name', 'John Doe');
return (
<div>
<h1>Hello, {name}!</h1>
<input
type="text"
value={name}
onChange={e => setName(e.target.value)}
/>
</div>
);
}
export default App;


Conclusion
Creating custom hooks in React is a powerful way to keep your code clean, reusable and easy to understand. The examples above show how you can encapsulate logic to handle localStorage and perform HTTP requests. Experiment with these hooks and create your own custom hooks to optimize your code and improve the maintainability of your applications.
Remember that custom hooks are not only a tool to reuse code, but also a way to improve the architecture and quality of your application. Explore, experiment and enjoy the power of custom hooks. custom hooks in React!
