How to wait for value before running fetch? - reactjs

Edit: I ended up using axios instead of fetch and it works great. Just removed the response.json() and switch fetch to axios.get.
my first post here with what is probably a pretty easy question. I am trying to get the lat and long values to actually be something before being fed into the URL. Most of the time I get an error returned for a bad request because the lat and long values haven't propagated yet.
Thanks!
Code is here (edited out API keys):
const fetchData = async () => {
navigator.geolocation.getCurrentPosition(function (position) {
setLat(position.coords.latitude);
setLong(position.coords.longitude);
});
const url =
await `https://api.openweathermap.org/data/2.5/weather/?lat=${lat}&lon=${long}&units=metric&APPID=DELETED`;
await fetch(url)
.then((response) => {
return response.json();
})
.then((result) => {
setData(result);
})
.catch(console.error);
};
fetchData();
}, [lat, long]);

It seems that lat and long are set in the useEffect using them. You should probably set them before using them in another useEffect.
useEffect(() => {
navigator.geolocation.getCurrentPosition(function (position) {
setLat(position.coords.latitude);
setLong(position.coords.longitude);
});
}, [])
useEffect(() => {
const fetchData = async () => {
const url = `https://api.openweathermap.org/data/2.5/weather/?lat=${lat}&lon=${long}&units=metric&APPID=DELETED`;
await fetch(url)
.then((response) => {
return response.json();
})
.then((result) => {
setData(result);
})
.catch(console.error);
};
if (lat && long) {
fetchData();
}
}, [lat, long]);

Either you have to store those values in your function or you have to wait until the state is updated. State is asynchronous and this is why you get this error.

Related

how to handle delay with setting useState with API data

I am not the first one to have this problem.
but sadly, nothing of what i saw is working for me.
I wanna get data from API.
use the data to call another API.
and than return the data in or whatever.
so what happens is, the first fetch is going fine. im trying to set it inside a state.
but when the SECOND API runs, the STATE i need, is still empty... it hasnt been updated.
so the component cant render something that doesnt exist. so it crashes.
and also, something here causes multiple renders...
here is the code:
const SecondDisplay = () => {
const [firstData, setFirstData] = useState("")
const [secondData, setSecondData] = useState("")
const [errors, setErrors] = useState("")
const [loading, setLoading] = useState(true)
useEffect(() => {
navigator.geolocation.getCurrentPosition(function(position) {
// getLocation(32.0853, 34.7818)
fetch(`https://dataservice.accuweather.com/locations/v1/cities/geoposition/search?apikey=AZvK08ugMNLlAGAwDD9GQGj108Tm8OIP&q=${position.coords.latitude}%2C${position.coords.longitude}&language=en-us&details=false&toplevel=false`)
.then(res => {
if(!res.ok){
throw Error("Sorry, something went wrong. please try again later.")
}
return res.json()
})
.then(data => {
setFirstData({key: data.Key, name: data.AdministrativeArea.EnglishName})
})
.catch(error => {
setErrors(error.message)
setLoading(false)
})
})
}, [])
useEffect(() => {
if(firstData !== ""){
third(firstData.key)
}
}, [firstData])
let third = key => {
fetch(`http://dataservice.accuweather.com/currentconditions/v1/${key}?apikey=AZvK08ugMNLlAGAwDD9GQGj108Tm8OIP&language=en-us&details=true HTTP/1.1`)
.then(res => {
if(!res.ok){
throw Error("Sorry, something went wrong. please try again later.")
}
return res.json()
})
.then(data => {
setSecondData(data)
setLoading(false)
})
.catch(error => {
setErrors(error.message)
setLoading(false)
})
}
return(<p>{secondData.blabla}</p>)
What you can do is use a single useEffect() hook, and simply continue the promise chain once you get the response from the first request.
Here's a simplified example:
fetch(url)
.then(res => {
if (!res.ok) throw Error(msg)
return res.json()
})
.then(data => {
setFirstData(data)
return fetch(secondUrl, paramsBasedOn(data))
})
.then(res => {
if (!res.ok) throw Error(msg)
return res.json()
})
.then(data => {
setSecondData(data)
})
.catch(err => setError(err.message))

React, Get Weather Aync Await Errors

Still teaching myself React, and was working on a project when I ran into an issue.
I have the below code.
const Weather = (props) => {
const [weather, setWeather] = useState([]);
const [lat, setLat] = useState([]);
const [long, setLong] = useState([]);
useEffect(() => {
const fetchData = async () => {
navigator.geolocation.getCurrentPosition(function (position) {
setLat(position.coords.latitude);
setLong(position.coords.longitude);
});
await fetch(`https://api.openweathermap.org/data/2.5/onecall?lat=${lat}&lon=${long}&appid=SomeID&units=imperial&exclude=minutely`
)
.then((res) => res.json())
.then((result) => {
setWeather(result);
console.log(result);
});
};
fetchData();
}, [lat, long]);
return (
<React.Fragment>
<div className={styles.weather}>
<div className={styles.weather__current}>{weather.current.temp}</div>
<div className={styles.weather__hourly}>Hourly</div>
<div className={styles.weather__daily}>5 Day</div>
</div>
</React.Fragment>
);
};
export default Weather;
My issue is that my request goes out twice before it has the lat and long "populated", on the third attempt it goes out just fine. So I get 2 responses of bad request, with the third being what I want.
My understanding is that in an Async function, the await, will "pause" until all previous variables are "satisfied". I may be misunderstanding that.
I can "hard code" the values, and everything is fine.
Any help would be appreciated.
The issue is that you are setting two of the useEffect's dependency inside the useEffect function itself (lat and long). You need to separate your useEffect into two separate ones: the first to fetch lat and long, the second to fetch the weather.
useEffect(() => {
navigator.geolocation.getCurrentPosition(function (position) {
setLat(position.coords.latitude);
setLong(position.coords.longitude);
});
}, []);
useEffect(() => {
fetch(`https://api.openweathermap.org/data/2.5/onecall?lat=${lat}&lon=${long}&appid=SomeID&units=imperial&exclude=minutely`)
.then((res) => res.json())
.then((result) => {
setWeather(result);
console.log(result);
});
}, [lat, long]);
Alternatively, since you are not rendering lat and long, you could just make them normal variables, not state variables:
useEffect(() => {
navigator.geolocation.getCurrentPosition(function (position) {
const lat = position.coords.latitude);
const long = position.coords.longitude);
fetch(`https://api.openweathermap.org/data/2.5/onecall?lat=${lat}&lon=${long}&appid=SomeID&units=imperial&exclude=minutely`)
.then((res) => res.json())
.then((result) => {
setWeather(result);
console.log(result);
});
});
}, []);
Your request is going twice because you are setting lat and long states before setting the weather and as you know everytime you set/change a state in a component, react updates it again.
The solution is to use useRef hook instead of useState for lat and long. You can also use const variable to store temporarily if not using these values anywhere else.

Call API only after setting loading state

Since, setState in a functional component do not return a promise, how do we set a loading state and then call an API. I have seen people doing it like the one below. I think the axios call will not wait for the loading state to be successfully set before executing. Is there any other better way to solve this without writing the fetch part in an useEffect with the dependency of the loading state?
useEffect(() => {
const fetchProduct = async () => {
setLoading(true);
try {
const response = await axios('http://localhost/products');
setData(response.data);
} catch (err) {
setError(err);
}
setLoading(false);
};
fetchProduct();
}, [productId]);
you can try something like this
useEffect(() => {
const fetchProduct = async () => {
setLoading(true);
await axios.get('http://localhost/products')
.then(response => {
setLoading(false);
setData(response.data);
}).catch(error => {
setLoading(false);
setError(error);
})
};
fetchProduct();
}, [productId]);

React Native - state is returning null after setting state

I'm very much new to react native currently i'm building small app for just getting an idea about this. I'm facing an issue in mapping the data from API. This is the json response returning from the api
{"data":[{"digit":300,"countsum":"52"},{"digit":301,"countsum":"102"},{"digit":302,"countsum":"27"},{"digit":303,"countsum":"201"},{"digit":500,"countsum":"101"}]}
When i tried to map this data i'm facing some issues. I stored the response from API to the state and when i tried to display the state data using map function it's showing the state value is null. This the code i tried till now
const [listdata, setListData] = useState(null)
useEffect(() => {
// Run! Like go get some data from an API.
getListData();
}, []);
const getListData = async () => {
const token = await AsyncStorage.getItem("#userToken")
axios
.get(constants.BASE_URL + "getlist?token=" +token)
.then(response => setListData(response.data))
.catch(error => {
console.log(error);
});
listdata.map(item => <Text>{item.digit}</Text>)
}
Do it like this,
export default function ComponentName () {
const [listdata, setListData] = useState([])
useEffect(() => {
// Run! Like go get some data from an API.
getListData();
}, []);
const getListData = async () => {
const token = await AsyncStorage.getItem("#userToken")
axios
.get(constants.BASE_URL + "getlist?token=" +token)
.then(response => setListData(response.data))
.catch(error => {
console.log(error);
});
}
return (<>
listdata.map(item => <Text>{item.digit}</Text>)
</>
);
}
You have to wait the fetch execution and later do the list map.
// wait for it
await axios
.get(constants.BASE_URL + "getlist?token=" +token)
.then(response => setListData(response.data))
.catch(error => {
console.log(error);
});
listdata.map(item => <Text>{item.digit}</Text>)
If you want to map the data then do that inside return statement of your code ,like:
return(
{listData?listdata.map(item => return <Text>{item.digit}</Text>):""}
);
This is a sample of a meant in my comment above:
Try console.log listdata at this stage, you will find that it is still
null, in other words, the value of the updated value of the
listdata:useSate will be ready after the render take place. You can
make another function outside of the current one. then use useEffect
with listdata to update your text views
const [listdata, setListData] = useState(null)
useEffect(() => makeRemoteRequest(), [listdata])
makeRemoteRequest = () => {
const url = `your-url-of-data-here`;
fetch(url)
.then(res => res.json())
.then(res => {
setListData(res.data);
})
.catch(error => {
console.log(error)
});
};
You could try the following:
const [listdata, setListData] = useState([])
useEffect(() => {
// Run! Like go get some data from an API.
getListData();
}, []);
const getListData = async () => {
const token = await AsyncStorage.getItem("#userToken")
try {
const dataResponse = await axios.get(constants.BASE_URL + "getlist?token=" +token);
setListData(dataResponse.data || [] );
} catch(error) {
console.log(error);
}
}
return (<>
listdata.map(item => <Text>{item.digit}</Text>)
</>);

How to use async function and export it correctly with React Native?

My question is about correctly implementing an async function to fetch data. I've a function called _getData() and I'm calling it on the componentDidMount() of a screen. But when server response is slow, switching to this screen is getting slower. So I would like to use async function for fetching data. But I'm not sure if I'm doing it correctly. Is that a correct approach? I can't be sure if it works async or not.
Here is my Api._getData() code:
const _getData = async () => {
return await axios.get("http://blabla.com/someservice", { params: someParamDataHere });
};
export const Api = {
_getData
};
and on SomeScreen.js, I also have loadData() function which calls the function above and does state updates.
loadData() {
Api._getData()
.then((response) => {
this.setState({ myData: response.data });
})
.catch((error) => {
console.log(error.response);
});
}
in componentDidMount() function of the same screen I'm calling this loadData()  function directly.
Now, is it enough to declare Api._getData() as async and using await in it, or should I change some trigger functions too?
Thank you very much for your help.
instead of async await use promises
export const getRequest = (url) => {
return new Promise((resolve, reject) => {
api
.get(url)
.then((response) => {
handleReponse(response)
.then((errorFreeResponse) => {
resolve(errorFreeResponse);
})
.catch((error) => {
reject(error);
});
})
.catch((error) => {
reject(handleError(error));
});
});
};
You are doing correct while retrieving in load Data . What you can do more is try more syntactical sugar of es6 by using async await in loadData , hence
loadData = async() =>{
try{
let response = await Api._getData();
this.setState({ myData: response.data });
} catch(err){
console.log(error.response);
}
}
Hope it helps. feel free for doubts

Resources