AsyncStorage store empty array after API fetch React Native - reactjs

I try to store data with AsyncStorage after fetch
const [items, setItems] = useState([]);
const fetchData = async () => {
try {
const response = await url.get(`/../`);
data = response.data;
if (data.length) {
setItems(data)
storeData();
}
} catch(error) {
console.log(error)
}
};
const storeData = async () => {
try {
const data = JSON.stringify(items);
await AsyncStorage.setItem(STORAGE_KEY, data);
} catch (error) {
alert('Error')
}
};
I call function fetchData with button. When i click first time, Asyncstorage store empty array.
On the second click it store an array.
<Button title="fetch data" onPress={fetchData} />
As much as i understood, it works asynchronously, but what should i change in this code to store data at first click?

setItems(data) is async , so you cant guarentee when the data will be set to state, a rather better approach would be like this :
const [items, setItems] = useState([]);
const fetchData = async () => {
try {
const response = await url.get(`/../`);
data = response.data;
if (data.length) {
setItems(data)
storeData(data);
}
} catch(error) {
console.log(error)
}
};
const storeData = async (data) => {
try {
const data1 = JSON.stringify(data);
await AsyncStorage.setItem(STORAGE_KEY, data1);
} catch (error) {
alert('Error')
}
};
here im passing the data to storeData and setting that value, hope it helps. please feel free for doubts

Related

I can't get Axios post information

For my posts
in component AboutUsers.jsx
const [users, setUsers] = useState([]);
if I write like this, it's working, I see posts in users:
in component AboutUsers.jsx
useEffect(()=> {
const getUsers = axios.get('https://jsonplaceholder.typicode.com/todos',{
params:{
_limit:limitPage,
_page:currentPage
}
})
.then(response => setUsers(response.data))
},[])
but I created other component PostMyServise.js with:
export default class PostMyServise {
static async getPost(limit=10, page=1) {
const result = await axios.get('https://jsonplaceholder.typicode.com/todos',{
params: {
_limit: limit,
_page: page,
}
})
.then(response => {
return response
})
return result;
}
}
And one yet component useCreatePosts.js:
export const usePosts = (callback) => {
const [isTrue, setIsTrue] = useState('')
const [error, setError] = useState('')
const createPost = async () => {
try {
setIsTrue(false);
await callback;
} catch (e) {
setError(e.message);
} finally {
setIsTrue(true);
}
}
return [createPost, isTrue, error];
}
export default usePosts;
I wrote this, and I see empty array in console.log(users):
I don't understand why array is empty
const [createPost, isTrue, error] = usePosts (async ()=> {
const response = await PostMyServise.getPost(limitPage, currentPage);
setUsers(response.data)
})
useEffect(() => {
createPost();
},[currentPage])
You are not calling the callback. You need to add the parentheses.
const createPost = async () => {
try {
setIsTrue(false);
await callback();
} catch (e) {
setError(e.message);
} finally {
setIsTrue(true);
}
}
I feel like something about your code is over-engineered and too complex but that's outside the scope of the question. This change will at least get it working. Also, I suggest changing the name of isTrue and setIsTrue to something more meaningful as those names do not tell you what they are for.

Infinite loop when setting and using state in a `useCallback` that is being called from a `useEffect`

I would like to fetch data when the user changes.
To do this I have a useEffect that triggers when the user changes, which calls a function to get the data.
The problem is that the useEffect is called too often because it has a dependency on getData and getData changes because it both uses and sets loading.
Are there ways around this, while still retaining getData as a function, as I call it elsewhere.
const getData = useCallback(async () => {
if (!loading) {
try {
setLoading(true);
const { error, data } = await getDataHook();
if (error) {
throw new Error("blah!");
}
} catch (error) {
const message = getErrorMessage(error);
setErrorMessage(message);
setLoading(false);
}
}
}, [loading]);
...
useEffect(() => {
const callGetData = async () => {
await getData();
};
callGetData();
}, [user, getData]);
Try moving loading from useCallback to useEffect. Something like this:
const getData = useCallback(async () => {
try {
const { error, data } = await getDataHook();
if (error) {
throw new Error("blah!");
}
} catch (error) {
const message = getErrorMessage(error);
setErrorMessage(message);
}
}, []);
...
useEffect(() => {
const callGetData = async () => {
await getData();
};
if (!loading) {
setLoading(true);
callGetData();
setLoading(false);
}
}, [user, getData, loading]);
The loading flag is something that the call sets, and shouldn't be effected by it, so remove it from the useEffect(), and getData() functions.
const getData = useCallback(async () => {
try {
setLoading(true);
const { error, data } = await getDataHook();
if (error) {
throw new Error("blah!");
}
} catch (error) {
const message = getErrorMessage(error);
setErrorMessage(message);
} finally {
setLoading(false); // not related, but this would remove loading after an error as well
}
}, []);
useEffect(() => {
const callGetData = async () => {
await getData(user);
};
callGetData();
}, [user, getData]);

React Hook useEffect has a missing dependency: 'fetchUser'. useEffect problem?

I'm new to react and I'm learning how to use useEffect. I encountered this warning in my react app. I tried out some solutions on SO but the warning still remains. Both fetchUser and fetchPosts trigger this warning. Can anyone enlighten me what is the problem and what does the warning mean?
App.js
useEffect(() => {
setLoading(true)
const getUser = async () => {
const userFromServer = await fetchUser()
if (userFromServer) {
setUser(userFromServer)
setLoading(false)
} else {
console.log("error")
}
}
getUser()
}, [userId])
useEffect(() => {
const getPosts = async () => {
const postsFromServer = await fetchPosts()
setPosts(postsFromServer)
}
getPosts()
}, [userId])
useEffect(() => {
const getUserList = async () => {
const userListFromServer = await fetchUserList()
setUserList(userListFromServer)
}
getUserList()
}, [])
// Fetch user
const fetchUser = async () => {
const res = await fetch(`https://jsonplaceholder.typicode.com/users/${userId}`)
const data = await res.json()
return data
}
// Fetch posts
const fetchPosts = async () => {
const res = await fetch(`https://jsonplaceholder.typicode.com/posts?userId=${userId}`)
const data = await res.json()
return data
}
// Fetch list of users
const fetchUserList = async () => {
const res = await fetch('https://jsonplaceholder.typicode.com/users/')
const data = await res.json()
return data
}
If you are using any function or state which has been declared outside the useEffect then you need to pass it in the dependency array like this:
const someFunctionA = () => {
....
}
const someFunctionB = () => {
....
}
useEffect(() => {
....
}, [someFunctionA, someFunctionB])
You can read more about it here in case you want to know how it will be rendered: React useEffect - passing a function in the dependency array

Combine two Axios calls inside one useEffect with async/await

I'm currently trying to understand how to work with async/await in React JS. In this demo app, I'm trying to get full border names of the chosen country by calling https://restcountries.eu/. I make first API call to get info about country and the second one to get full name of its borders since first API call returns
only short border names. I believe there is a way to combine those calls inside one useEffect however everything I tried gave me some sort of an error or getting stuck in infinite loop. What is the proper way to combine those calls with async/await approach?
import React, { useState, useEffect } from "react";
import Axios from "axios";
const App = () => {
const [loading, setLoading] = useState(true);
const [country, setCountry] = useState({});
const [fullBorderNames, setFullBorderNames] = useState([]);
//FIRST API CALL
useEffect(() => {
const source = Axios.CancelToken.source();
const fetchData = async () => {
setLoading(true);
try {
const response = await Axios(
`https://restcountries.eu/rest/v2/name/canada?fullText=true`,
{ cancelToken: source.token }
);
setCountry(response.data[0]);
} catch (err) {
if (Axios.isCancel(err)) {
console.log("Axios canceled");
} else {
console.log(err);
}
}
};
fetchData();
return () => source.cancel();
}, []);
//SECOND API CALL
useEffect(() => {
const source = Axios.CancelToken.source();
let borders = [];
if (country.borders) {
const fetchData = async () => {
try {
country.borders.forEach(async border => {
const response = await Axios(
`https://restcountries.eu/rest/v2/alpha?codes=${border}`,
{ cancelToken: source.token }
);
borders.push(response.data[0].name);
if (borders.length === country.borders.length)
setFullBorderNames(borders);
});
} catch (err) {
if (Axios.isCancel(err)) {
console.log("Axios canceled");
} else {
console.log(err);
}
}
setLoading(false);
};
fetchData();
}
return () => source.cancel();
}, [country.borders]);
if (loading) {
return <h2>Loading</h2>;
} else {
return (
<>
<pre>{JSON.stringify(country, null, 2)}</pre>
<pre>{JSON.stringify(fullBorderNames, null, 2)}</pre>
</>
);
}
};
export default App;
You can simply just make the requests right after the first one.
try {
const response = await Axios(`https://restcountries.eu/rest/v2/name/canada?
fullText=true`, { cancelToken: source.token });
const country = response.data[0];
setCountry(country);
/* all the other fetch calls*/
Can you tell me what kind of errors you get because I don't see an issue with doing them in the same useEffect? It just gets a little messy which can be refactored anyway.

It seems Axios does not update the state when use with async and await

How do I update the state data immediately when I use Axios with async and await? It seems not updating the state data immediately. Many thanks in advance and greatly appreciated. Here is the code sample:
const[dbdata,setDBData] = useState([])
useEffect(async() => {
const response = await Axios.get('http://localhost:5000/api/posts/allpost', {withCredentials:true})
setDBData(response.data)
}, [])
const GetPost = async(id) =>{
const response = await Axios.put('http://localhost:5000/api/posts/getPost',{postId:id}, {withCredentials:true})
const getData = dbdata.map(item => {
if(item._id==response._id){
return response
}
else{
return item
}
})
console.log(getData)
setDBData(getData)
}
useEffect(async () => ...) are not supported, but you can call an async function inside an effect.
Try:
useEffect(() => {
const GetPost = async(id) =>{
const response = await Axios.put('http://localhost:5000/api/posts/getPost',{postId:id}, {withCredentials:true});
const getData = dbdata.map(item => {
if(item._id==response._id){
return response;
}
else{
return item;
}
})
console.log(getData);
setDBData(getData);
}
GetPost();
}, [])
EDIT:
OR:
useEffect(() => {
GetPost();
}, []);
const GetPost = async(id) =>{
const response = await Axios.put('http://localhost:5000/api/posts/getPost',{postId:id}, {withCredentials:true});
const getData = dbdata.map(item => {
if(item._id==response._id){
return response;
}
else{
return item;
}
})
console.log(getData);
setDBData(getData);
}

Resources