skip to Main Content

I created a custom hook in my Nextjs application to send a request using Axios.

Here is my useApiRequest file:

import { ErrorResponse, SuccessResponse } from "@/types/api";
import axios, { AxiosResponse } from "axios";
import { useState } from "react";

const useApiRequest = <T>(
  httpMethod: string,
  url: string,
  data?: any
): [boolean, T | string, string] => {
  const [response, setResponse] = useState<[boolean, T | string, string]>([
    false,
    "",
    "",
  ]);

  const fetchData = async () => {
    try {
      const apiResponse: AxiosResponse<SuccessResponse<T> | ErrorResponse> =
        await axios({
          method: httpMethod,
          url: url,
          data: data,
        });

      if ("result" in apiResponse.data && "message" in apiResponse.data) {
        const successData: SuccessResponse<T> = apiResponse.data;
        setResponse([true, successData.result, successData.message]);
      } else {
        const errorData: ErrorResponse = apiResponse.data;
        setResponse([false, errorData.error, errorData.error]);
      }
    } catch (error) {
      if (error instanceof Error) {
        setResponse([false, "", error.message]);
      } else {
        setResponse([false, "", "Terjadi kesalahan"]);
      }
    }
  };

  fetchData();

  return response;
};

export default useApiRequest;

I have used that hook in one of my pages like this:

import Button from "@/components/elements/buttons/Button";
import useApiRequest from "@/hooks/useApiRequest";
import Head from "next/head";

const JadwalPage = () => {
  const handleLogin = () => {
    const [isSuccess, responseData, message] = useApiRequest(
      "post",
      "/auth/login",
      {
        username: "admin",
        password: "pratama-putra",
      }
    );

    if (isSuccess) {
      console.log("Data berhasil diperoleh:", responseData);
    } else {
      console.error("Gagal melakukan permintaan:", message);
    }
  };

  return (
    <div className="min-h-screen">
      <Head>
        <title>Jadwal Karyawan</title>
      </Head>
      <Button onClick={handleLogin}>Click me</Button>
    </div>
  );
};

export default JadwalPage;

Why do I always get an error Uncaught Error: Invalid hook call. Hooks can only be called inside of the body of a function component.

I tried to change useApiRequest from an arrow function to a regular function, but it still produces the same error.

2

Answers


  1. You need to use use Hooks at the top level of your React function. From the React documentation-

    Hooks are JavaScript functions, but you need to follow two rules when using them.
    Don’t call Hooks from regular JavaScript functions. Instead, you can:

    ✅ Call Hooks from React function components. ✅ Call Hooks from custom
    Hooks

    Only Call Hooks from React Functions

    Login or Signup to reply.
  2. It’s not supported to call Hooks (functions starting with use) in any other cases, for example:

    • Do not call Hooks inside conditions or loops.
    • Do not call Hooks after a conditional return statement.
    • Do not call Hooks in event handlers.
    • Do not call Hooks in class components.
    • Do not call Hooks inside functions passed to useMemo, useReducer, or
      useEffect.

    You should use your hook at the top level of your component. You can create behaviour like mutation. So whenever you call a hook it will return a function which can be further used to make API calls.

    import { ErrorResponse, SuccessResponse } from "@/types/api";
    import axios, { AxiosResponse } from "axios";
    import { useState } from "react";
    
    const useApiRequest = <T>(
      httpMethod: string,
      url: string,
      data?: any
    ): [boolean, T | string, string] => {
      const [response, setResponse] = useState<[boolean, T | string, string]>([
        false,
        "",
        "",
      ]);
    
      const fetchData = async () => {
        try {
          const apiResponse: AxiosResponse<SuccessResponse<T> | ErrorResponse> =
            await axios({
              method: httpMethod,
              url: url,
              data: data,
            });
    
          if ("result" in apiResponse.data && "message" in apiResponse.data) {
            const successData: SuccessResponse<T> = apiResponse.data;
            setResponse([true, successData.result, successData.message]);
          } else {
            const errorData: ErrorResponse = apiResponse.data;
            setResponse([false, errorData.error, errorData.error]);
          }
        } catch (error) {
          if (error instanceof Error) {
            setResponse([false, "", error.message]);
          } else {
            setResponse([false, "", "Terjadi kesalahan"]);
          }
        }
      };
    
      return {
        fetchData,
        response
      };
    };
    
    export default useApiRequest;
    

    Use it like below:

    import Button from "@/components/elements/buttons/Button";
    import useApiRequest from "@/hooks/useApiRequest";
    import Head from "next/head";
    
    const JadwalPage = () => {
      // Use hook at top level
      const { fetchData, response } = useApiRequest(
          "post",
          "/auth/login",
          {
            username: "admin",
            password: "pratama-putra",
          }
        );
        
    
      const handleLogin = async () => {
        // Call your API 
        await fetchData();
    
        // Use/Process your data 
    
      }
        
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search