Custom hooks become a staple in our lives after we got introduced to hooks such as useEffect, useState, useReducer. Regardless of their purposes, custom hooks should start with use
such as useAxiosAsync
or useToggle
. Therefore we can easily perceive that this is a custom hook. We could name them differently, but that would make them harder to detect at the first sight this is why the community following that convention.
Like in any other area of programming we always want to make things re-usable by detaching them from the main code. We, either turn them into functions or classes, but we are dealing with states so we want to be able to utilize react related stuff as well.
Thus we can achieve a couple of things:
- DRY(Don't repeat yourself)
- Reduced coupling
- Modular code
- Testable code
Let's take a look at this easy example:
useToggle
import { Alert, Button, Flex, Text } from '@chakra-ui/react';
function App() {
const [on, toggle] = React.useState(false);
return (
<div>
<span>{on ? 'ON' : 'OFF'}</span>
<button onClick={() => toggle((prevState) => !prevState)}>TOGGLE</button>
<button onClick={() => toggle(true)}>SET TOGGLE ON</button>
<button onClick={() => toggle(false)}>SET TOGGLE OFF</button>
</div>
);
}
export default App;
This example is very different from where we toggle for Loading
state while making a request for our APIs. Just on and off it's that simple. If we want this to be a custom hook, all we have to do is take useState
out of this component and check for conditions while toggling state.
import { useState } from 'react';
const useToggle = (initialState = false) => {
const [toggle, setToggle] = useState(initialState);
const toggleState = (state?: false | true) => {
setToggle(typeof state === 'boolean' ? state : !toggle);
};
return [toggle, toggleState] as const;
};
export default useToggle;
Now, using our custom hook in the same component as we did before.
import { Alert, Button, Flex, Text } from '@chakra-ui/react';
import useToggle from './useToggle';
function ToggleWithHook() {
const [on, toggle] = useToggle(false);
return (
<div>
<span>{on ? 'ON' : 'OFF'}</span>
<button onClick={() => toggle()}>TOGGLE</button>
<button onClick={() => toggle(true)}>SET TOGGLE ON</button>
<button onClick={() => toggle(false)}>SET TOGGLE OFF</button>
</div>
);
}
export default ToggleWithHook;
Let's take a look at a more complex example, where we also use Generics
in Typescript.
useAxiosAsync
type JokeType = {
created_at: string;
icon_url: string;
id: string;
url: string;
value: string;
};
function AsyncWithoutHook() {
const [joke, setJoke] = useState<JokeType | null>(null);
const [error, setError] = useState<AxiosError<Error> | null>(null);
const [isLoading, setIsLoading] = useState<boolean>(false);
const fetchAJoke = async () => {
setIsLoading(true);
try {
const { data } = await axios.get<JokeType>('https://api.chucknorris.io/jokes/random');
setJoke(data);
setIsLoading(false);
} catch (error) {
setError(error);
setIsLoading(false);
}
};
useEffect(() => {
fetchAJoke();
}, []);
if (error) return <Flex>{error}</Flex>;
if (isLoading) return <Flex>Loading...</Flex>;
return (
<Flex flexDirection="column" boxShadow="outline" rounded="md" width="50%" padding="1rem">
{joke?.value}
<Button onClick={fetchAJoke}>Fetch Again!!!</Button>
</Flex>
);
}
export default AsyncWithoutHook;
Now, using custom hooks, again.
import { Button, Flex } from '@chakra-ui/react';
import React from 'react';
import useAxiosAsync from './useAxiosAsync';
type JokeType = {
created_at: string;
icon_url: string;
id: string;
url: string;
value: string;
};
function AsyncWithHook() {
const [{ data, isLoading, error }, fetchAJoke] = useAxiosAsync<JokeType>(
'https://api.chucknorris.io/jokes/random',
);
if (error) return <Flex>{error}</Flex>;
if (isLoading) return <Flex>Loading...</Flex>;
return (
<Flex flexDirection="column" boxShadow="outline" rounded="md" width="50%" padding="1rem">
{data?.value}
<Button onClick={fetchAJoke}>Fetch Again!!!</Button>
</Flex>
);
}
export default AsyncWithHook;
We've shortened our component quite a bit by moving state related things to our useAxiosAsync
. So, we no longer need to deal with business logic in our component and of course, we can use this useAxiosAsync
whenever we want.
Roundup
In the essence, custom hooks are just useState
and useEffect
(of course other hooks as well) hooks moved to another file to reduce the amount of code in components.
So, if something bloating your component or you keep using same logic in other components, it's time to convert it into custom hook.
Thanks for reading 🥳🥳🥳.
Top comments (0)