I'm trying to fetch an api on a custom reactjs hook using Axios. I keep getting twice the response as undefined and after that twice as a successful fetch with the data. The undefined breaks my app.
Btw I'm fetching from the randomuser api.
import axios from "axios";
import { useState, useEffect } from "react"
export const useFetch = (url) => {
const [loading, setLoading] = useState(false);
const [data, setData] = useState([]);
const [error, setError] = useState('')
const getData = () => {
setLoading(true)
try {
axios.get(url)
.then(response => setData(response.data));
setLoading(false)
} catch (error) {
setError(error)
}
};
useEffect(() => {
getData()
}, [url])
return {loading, data, error}
}
Trying to use it here and map over it
import React, { useEffect, useState } from 'react';
import { useParams } from 'react-router-dom';
import { useFetch } from '../custom_hooks/useFetch';
const PersonDetails = () => {
const { loading, data , error } = useFetch('https://randomuser.me/api?results=20');
const { results } = data;
const { id } = useParams();
const [person, setPerson] = useState({})
useEffect(() => {
const newPerson = results?.find(person => person.login.uuid === parseInt(id))
setPerson(newPerson)
console.log(newPerson)
}, [])
return (
<div>
{person.name.first}
</div>
)
}
export default PersonDetails
This is the thing I actually Im trying to do, but now because it is undefined, I get that cannot read properties of undefined...
CodePudding user response:
When the effect runs you:
setLoading(true)- Send the Ajax request
setLoading(false)
Later, then the Ajax response arrives you:
setData(response.data)
Since you depend on loading to determine if data is set or not, it breaks.
There are two things you could do:
- Move
setLoading(false)inside thethencallback so it doesn't get set until after you havesetData(response.data) - Get rid of
loadingentirely and base your logic offdatabeingundefinedor having a different value.
CodePudding user response:
- you should define the getData function inside the useeffect or pass it in dependency array and wrap the function by usecallback to avoid unnecessary rerenders.
- you should use abortcontroller in case of cancelations and to have cleanup function in useeffect. (in this case it's better to define getdata body in useeffect)
useEffect(() => {
const controller = new AbortController();
const getData = async () => {
setLoading(true)
try {
await axios.get(url, {signal: controller.signal})
.then(response => setData(response.data));
} catch (error) {
setError(error)
}
}
getData()
return()=>controller.abort()
},[url]}
you can read more about fetching data with hooks in this url: https://www.robinwieruch.de/react-hooks-fetch-data/
CodePudding user response:
fetching data takes time, console.log doesn't wait for the response, and it prints undefined, ** keep that in mind fetch is an asynchronous task **
