I’m doing a React project. I have two reutilizable functions that made requests to an API. When I call the functions, react raise the error:
Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
- You might have mismatching versions of React and the renderer (such as React DOM)
- You might be breaking the Rules of Hooks
- You might have more than one copy of React in the same app
I have fetchCustomers.jsx:
export default function fetchCustomers() {
const [customers, setCustomers] = useState([]);
useEffect(() => {
const fetchData = async () => {
try {
const response = await axios.get('customers/', {
headers: {
'Authorization': `Bearer ${localStorage.getItem('access_token')}`
}
});
setCustomers(response.data);
} catch (error) {
console.error("Error al obtener los datos de clientes.", error);
}
};
fetchData();
}, []);
return customers;
}
fetchSales.jsx:
export default function fetchSales(params = {}) {
const [sales, setSales] = useState([]);
useEffect(() => {
const fetchData = async () => {
try {
const response = await axios.get('sales/', {
params: params,
headers: {
'Authorization': `Bearer ${localStorage.getItem('access_token')}`
}
});
setSales(response.data.results);
} catch (error) {
console.error("Error al obtener los datos de ventas.", error);
}
};
fetchData();
}, []);
return sales;
}
and cobrar.jsx:
export default function Cobrar() {
const [filter, setFilter] = useState({
startDate: null,
endDate: null,
customer: null,
uncharged: true,
});
const [customers, setCustomers] = useState([]);
const [sales, setSales] = useState([]);
useEffect(() => {
const fetchData = async () => {
try {
const customersResponse = fetchCustomers();
setCustomers(customersResponse);
const salesResponse = fetchSales();
setSales(salesResponse);
} catch (error) {
console.error("Error al obtener los datos.", error);
}
}
fetchData();
}, []);
I tried call the functions in the const and works but I can’t filter the sales by params. I want to get all the sales and then filter by daterange and some fields.
>Solution :
fetchCustomers and fetchSales are written as hooks, not as otherwise regular functions. First, rename them to useCustomers and useSales (to be consistent with the naming for hooks). Then, use them as hooks in your component:
export default function Cobrar() {
const [filter, setFilter] = useState({
startDate: null,
endDate: null,
customer: null,
uncharged: true,
});
const customers = useCustomers();
const sales = useSales();
// the rest of this component...
You don’t need to repeat the usage of state because the hooks internally maintain their state.
Alternatively, if you don’t want these functions to be custom hooks, remove their internal use of hooks and just make them functions that return results directly. For example:
export default async function fetchCustomers() {
const response = await axios.get('customers/', {
headers: {
'Authorization': `Bearer ${localStorage.getItem('access_token')}`
}
});
return response.data;
}
Then you can call the function in any context without an invalid hook call, since the function no longer tries to use hooks.
Overall:
- Don’t double-up on tracking data in state. Keeping multiple data sources synchronized is a hard problem to solve, and an unnecessary one. Track the state in the component or in a hook, not both.
- Hooks can only be called from components or other hooks, and must always be called in the same order when rendering the component/hook. They can’t be called conditionally, within functions, etc.