Home > database >  Component keeps making api calls with custom axios hook
Component keeps making api calls with custom axios hook

Time:05-22

I have a dashboard component which renders some widget. It fetches widget data from four different APIs.

Dashbaord.jsx

import { Box, Stack } from '@mui/material';
import { useAxios } from '../../api/use-axios';
import { NewWidget } from '../../components/widget/NewWidget';
import ApiConfig from '../../api/api-config';

const Dashboard = () => {
  const { response: studentResponse } = useAxios({
    url: ApiConfig.STUDENTS.base,
  });

  const { response: courseResponse } = useAxios({
    url: ApiConfig.COURSES.base,
  });

  const { response: feesResponse } = useAxios({
    url: ApiConfig.FEES.total,
  });

  return (
    <Box padding={2} width="100%">
      <Stack direction={'row'} justifyContent="space-between" gap={2} mb={10}>
        <NewWidget type={'student'} counter={studentResponse?.data?.length} />
        <NewWidget type={'course'} counter={courseResponse?.data?.length} />
        <NewWidget type={'earning'} counter={feesResponse?.data} />
        <NewWidget type={'teacher'} counter={studentResponse?.data?.length} />
      </Stack>
    </Box>
  );
};
export default Dashboard;

It uses a custom hook useAxios to make API calls.

use-axios.jsx

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

axios.defaults.baseURL = 'http://localhost:3000';

export const useAxios = (axiosParams) => {
  const [response, setResponse] = useState(undefined);
  const [error, setError] = useState('');
  const [loading, setLoading] = useState(true);

  const fetchData = async (params) => {
    try {
      const result = await axios.request({
        ...params,
        method: params.method || 'GET',
        baseURL: 'http://localhost:3000',
        headers: {
          accept: 'application/json',
        },
      });
      setResponse(result.data);
    } catch (error) {
      setError(error);
    } finally {
      setLoading(false);
    }
  };

  useEffect(() => {
    fetchData(axiosParams);
  }, [axiosParams]); // execute once only

  return { response, error, loading };
};

api-config.js

export default {
  COURSES: {
    base: '/courses',
  },
  FEES: {
    base: '/fees',
    total: '/fees/total',
  },
  STUDENTS: {
    base: '/students',
  },
};

But somehow, It keeps rendering and also all the responses form APIs, it logs to undefiend.

I tried removing dependency axiosPamras from useEffect in useAxios, It stops making multiple requests but still it shows dependency warning and also response is still undefined.


Update: undefined error is fixed, I wasn't passing authorization token. :(

But still when axiosParams added to dependency it keeps calling apis in loop


CodePudding user response:

This is happening because of the way you're calling useAxios. You're passing an object literal each time, eg

const { response: studentResponse } = useAxios({
  url: ApiConfig.STUDENTS.base,
});

Because you're calling with an object, equality of this is determined by reference - and passing an object literal is a new reference on each render, even though it's "the same" object as far as you're concerned. So the useEffect with axiosParams as its dependency will rerun each time, hence the repeated sending of requests.

The easiest solution in this case is probably to extract these objects to constants which are stored outside the component - they come from an ApiConfig object so it seems unlikely this will change while the application is running. And doing this will mean the reference will always be the same and thus not trigger your useEffect to rerun.

That is, put this outside the component:

const STUDENT_AXIOS_CONFIG = { url: ApiConfig.STUDENTS.base };

and the same for the other 2 sets of axios Params. Then inside the component do:

const { response: studentResponse } = useAxios(STUDENT_AXIOS_CONFIG);

and of course do the same for the other 2 calls.

  • Related