DEV Community

Cover image for Introduction to Custom Hooks - useToggle & useAxiosAsync
Oğuzhan Olguncu
Oğuzhan Olguncu

Posted on • Originally published at ogzhanolguncu.com

Introduction to Custom Hooks - useToggle & useAxiosAsync

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;
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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)