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.